函数对象的bind()方法会创建一个新函数,bind()方法返回由指定的this值和初始化参数改造的原函数拷贝。
语法 fun.bind(thisArg[, arg1[, arg2[, ...]]])
比如foo.bind(bar, arg1, arg2)
,它返回一个新函数,就是说,内存中出现了一个匿名的新函数,暂且叫他newfoo吧,未来newfoo被调用的时候,它的this值指向bar的引用,newfoo的参数是foo的所有参数。
下面是一个最简单的例子,可以看到没啥奇怪的,this.x的this指向的是调用方法的对象,也就是module,module的x属性的值是81,所以输出81。
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
console.log(module.getX()); // 81
然后,一个新手想把module.getX方法赋值给一个新的变量,然后打算以后用这个变量来运行function() { return this.x; }
,于是他写成了:
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 返回 81
var retrieveX = module.getX;
console.log(retrieveX()); // 返回 9
为什么返回9?因为var retrieveX = module.getX相当于var retrieveX = function() { return this.x; },这时候this指向window,所以返回9。
这下是不是傻逼了?怎么避免这种傻逼?给module.getX方法绑定新的this指向就可以了。
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 返回 81
var retrieveX = module.getX.bind(module);
console.log(retrieveX()); // 返回 81
也就是说,retrieveX函数的this依然指向module。
bind()的另一个最简单的用法是,使一个函数拥有预设的初始参数。这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
比如,我们现有一个很简单的做加法的函数叫foo:
function foo(a, b) {
return a + b;
}
我开始用这个函数解决各种问题,突然有个场景是,我已知第一个参数是41,第二个参数不确定。那么我是不是要重新写一个几乎一模一样的函数呢?当然No。由于我举例的foo函数特别简单,你当然觉得重写一下也不费什么劲,但如果实践中foo函数很长很复杂呢?现在有bind,就可以一句创建出一个新函数。
function foo(a, b) {
return a + '--' + b;
}
var bar = foo.bind(null, 41); // 第一个参数是null或者undefined的话,bar的this不改变指向,只是第一个参数永远传入41
console.log(bar(3)); // 41--3,第一个参数是41,第二个是3。
当然你可能说,不用bind()也可以实现,比如下面用高阶函数实现:
function foo(a, b) {
return a + '--' + b;
}
function bar(b) {
return foo(41, b);
};
console.log(bar(3)); // 41--3
bind()和高阶函数两种都可以,bind()算是另一套解决方案。
现在我又有新要求,页面有一个按钮,点击之后3秒之后打印按钮上的文字,不注意this指向的话,会是下面这样:
document.getElementById('btn').onclick = function() {
var foo = function() {
console.log(this.innerText); // undefined
};
setTimeout(foo, 3000);
}
怎么改?foo的bind方法传入的第一个参数就是点击事件的回调函数,回调函数的this指向按钮DOM对象,所以foo能输出按钮上的文字。
document.getElementById('btn').onclick = function() {
var foo = function() {
console.log(this.innerText); // undefined
};
setTimeout(foo.bind(this), 3000);
}