function.bind
function.bind
bind()
方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
语法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数
thisArg
当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new
操作符调用绑定函数时,该参数无效。
返回值
返回由指定的this值和初始化参数改造的原函数拷贝
描述
bind() 函数会创建一个新函数
(称为绑定函数),新函数
与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call
属性)。当新函数
被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。
新函数具有下列内部属性:
[BoundTargetFunction]
- 封装的函数对象;
[BoundThis]
- 在调用包装函数时作为此
值传递的值。
[BoundArguments]
- 元素被用作对包装函数的调用的第一个参数的值列表。
[call]
- 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this
值和一个包含通过调用表达式传递给函数的参数的列表。
当调用绑定函数时,它调用[BoundTargetFunction]
上的内部方法[Call ],
并使用以下参数Call(boundThis,
args)。
其中,boundThis
是[BoundThis]
,args
是[BoundArguments]
后跟函数调用传递的参数。
绑定函数也可以使用new
运算符来构造:这样做就好像目标函数已被构建一样。所提供的this
值将被忽略,同时为仿真函数提供前置参数。
示例
创建绑定函数
bind()
最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的 this
值。JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,希望方法中的 this
是原来的对象。(比如在回调中传入这个方法。)如果不做特殊处理的话,一般会丢失原来的对象。从原来的函数和原来的对象创建一个绑定函数,则能很漂亮地解决这个问题:
this.x = 9; // this refers to global "window" object here in the browser
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX( // 81
var retrieveX = module.getX;
retrieveX(
// returns 9 - The function gets invoked at the global scope
// Create a new function with 'this' bound to module
// New programmers might confuse the
// global var x with module's property x
var boundGetX = retrieveX.bind(module
boundGetX( // 81
偏函数(Partial Functions)
bind()
的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数(如果有的话)作为bind()
的第二个参数跟在this
(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
function list() {
return Array.prototype.slice.call(arguments
}
var list1 = list(1, 2, 3 // [1, 2, 3]
// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(null, 37
var list2 = leadingThirtysevenList(
// [37]
var list3 = leadingThirtysevenList(1, 2, 3
// [37, 1, 2, 3]
配合 setTimeout
在默认情况下,使用window.setTimeout()
时,this
关键字会指向window
(或全局)对象。当使用类的方法时,需要 this
引用类的实例,你可能需要显式地把 this
绑定到回调函数以便继续使用实例。
function LateBloomer() {
this.petalCount = Math.floor(Math.random() * 12) + 1;
}
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!'
};
var flower = new LateBloomer(
flower.bloom(
// after 1 second, triggers the 'declare' method
作为构造函数使用的绑定函数
警告
:这部分演示了 JavaScript 的能力并且记录了bind()
的超前用法。以下展示的方法并不是最佳的解决方案且可能不应该用在任何生产环境中。
自然而然地,绑定函数适用于用new
操作符 new
去构造一个由目标函数创建的新的实例。当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。然而, 原先提供的那些参数仍然会被前置到构造函数调用的前面。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return this.x + ',' + this.y;
};
var p = new Point(1, 2
p.toString( // '1,2'
// not supported in the polyfill below,
// works fine with native bind:
var YAxisPoint = Point.bind(null, 0/*x*/
var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/
var axisPoint = new YAxisPoint(5
axisPoint.toString( // '0,5'
axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // true
你知道不需要做特别的处理就可以用new
操作符 new
创建一个绑定函数。必然地,你需要知道不需要做特别处理就可以创建一个可以被直接调用的绑定函数,即使你更希望绑定函数是用new
操作符 new
来调用。
// Example can be run directly in your JavaScript console
// ...continuing from above
// Can still be called as a normal function
// (although usually this is undesired)
YAxisPoint(13
emptyObj.x + ',' + emptyObj.y;
// > '0,13'
如果你希望一个绑定函数只支持使用new
操作符 new
,或者只能直接调用它,那么模板函数必须强制执行那限制。
快捷调用
在你想要为一个需要特定的 this 值的函数创建一个捷径(shortcut)的时候,bind()
方法也很好用。
你可以用Array.prototype.slice
来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以创建这样一个捷径:
var slice = Array.prototype.slice;
// ...
slice.apply(arguments
用bind()
可以使这个过程变得简单。在下面这段代码里面,slice
是 Function.prototype
的 apply()
方法的绑定函数,并且将Array.prototype
的 slice()
方法作为 this
的值。这意味着我们压根儿用不着上面那个 apply()
调用了。
// same as "slice" in the previous example
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice
// ...
slice(arguments
Polyfill
您可以通过在脚本的开头插入以下代码来解决部分问题,从而允许bind()
在本机不支持的实现中使用许多功能。
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments))
};
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP(
return fBound;
};
}
上述算法和实际的实现算法还有许多其他的不同 (尽管可能还有其他不同之处,却没有那个必要去穷尽):
- 这部分实现依赖于
Array.prototype.slice()
,Array.prototype.concat()
,Function.prototype.call()
这些原生方法。
- 这部分实现创建的函数的实现并没有
caller
以及会在 get,set或者deletion上抛出TypeError
错误的 arguments 属性这两个不可改变的“毒药” 。(假如环境支持{jsxref("Object.defineProperty")}}, 或者实现支持__defineGetter__
and__defineSetter__
扩展)
- 这部分实现创建的函数有
prototype
属性。(正确的绑定函数没有的)
- 这部分实现创建的绑定函数所有的 length 属性并不是同ECMA-262标准一致的:它的 length 是0,而在实际的实现中根据目标函数的 length 和预先指定的参数个数可能会返回非零的 length。
如果你选择使用这部分实现,你不能依赖于ECMA-262,但是ECMA-5是可以的。
在某些情况下(也可以作另一番修改以适应特定的需要),这部分实现也许可以作为一个过渡,在bind()
函数被广泛支持之前。
请查看https://github.com/Raynos/function-bind以获得更彻底的解决方案!
规范
Specification | Status | Comment |
---|---|---|
ECMAScript 5.1 (ECMA-262)The definition of 'Function.prototype.bind' in that specification. | Standard | Initial definition. Implemented in JavaScript 1.8.5. |
ECMAScript 2015 (6th Edition, ECMA-262)The definition of 'Function.prototype.bind' in that specification. | Standard | |
ECMAScript Latest Draft (ECMA-262)The definition of 'Function.prototype.bind' in that specification. | Living Standard | |
浏览器兼容性
Feature | Chrome | Edge | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
Basic support | 7 | (Yes) | 4.0 (2) | 9 | 11.60 | 5.1 |
Feature | Android | Chrome for Android | Edge | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|---|
Basic support | 4.0 | 1 | (Yes) | 4.0 (2) | ? | 11.5 | 6.0 |