简介
bind() 方法会创建一个新函数,当这个新函数被调用时,其 this 指向传递给 bind() 的第一个参数,其参数是传递给bind() 的其他参数加上其原本参数。
语法
fun.bind(thisArg[, arg1, arg2, ...])
参数
- thisArg 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效。
- arg1, arg2, … (可选)当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。
示例
function fn(a, b, c) {
return a + b + c;
}
var _fn = fn.bind(null, 10);
var ans = _fn(20, 30); // 60
//fn 函数需要三个参数,_fn 函数将 10 作为第一个参数,20和30作为后两个参数。
var ans = _fn(20, 30, 40); // 60
// 如果传入三个参数,也只取前两个!
作用
如果某些函数,其参数中的一部分已经 “预设” ,便可以用 bind 返回一个新的函数。即 bind() 能使一个函数拥有预设的初始参数。这些参数(如果有的话)作为 bind() 的第二个(或更多)参数跟在 this 后面,在目标函数被调用时插入到参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
new
当使用 new 操作符调用绑定函数时,bind 的第一个参数无效。
function Person(name, age) {
this.name = name;
this.age = age;
}
var _Person = Person.bind({});
var p = new _Person('hanzichi', 30); // Person {name: "hanzichi", age: 30}
setTimeout
在使用 setTimeout 编程时,传入的参数方法 this 指向容易丢失 或 指向 window,setInterval 也是一样,使用bind方法则能够显式地把 回调函数的this 绑定指定对象。
var canvas = {
render: function() {
this.update();
this.draw();
},
update: function() {
// ...
},
draw: function() {
// ...
}
};
window.setInterval(canvas.render, 1000 / 60);
用 canvas 写特效或者做游戏时经常会碰到类似的问题。上面的代码 中 计时器回调执行时,render 方法的 this 其实被指向了 window!用 bind 显式地把 this 绑定到canvas以便继续使用该对象。
window.setInterval(canvas.render.bind(canvas), 1000);
类似的情况还有 dom 的事件监听,一不小心可能 this 就被指向了 dom 元素。
tip
bind 还能做一些有意思的事情。
通常来说,将一个类数组转为数组,我们会用 slice(ie9- 不支持)。参考#14
var slice = Array.prototype.slice;
// slice.apply(arguments);
// slice(arguments, 1);
bind 能让调用变的更加简单。
// same as "slice" in the previous example
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
// ...
slice(arguments);
// slice(arguments, 1);
再举个类似的例子,比如说我们要添加事件到多个节点,for 循环当然没有任何问题,我们还可以 “剽窃” forEach
方法:
Array.prototype.forEach.call(document.querySelectorAll('input[type="button"]'), function(el){
el.addEventListener('click', fn);
});
更进一步,我们可以用 bind 将函数封装的更好:
var unboundForEach = Array.prototype.forEach
, forEach = Function.prototype.call.bind(unboundForEach);
forEach(document.querySelectorAll('input[type="button"]'), function (el) {
el.addEventListener('click', fn);
});
同样类似的,我们可以将 x.y(z) 变成 y(x,z) 的形式:
var obj = {
num: 10,
getCount: function() {
return this.num;
}
};
var unboundBind = Function.prototype.bind
, bind = Function.prototype.call.bind(unboundBind);
var getCount = bind(obj.getCount, obj);
console.log(getCount()); // 10
再举个栗子。每隔一秒在控制台打印 1-5,看起来是道考察闭包的经典题目。
for(var i = 1; i <= 5; i++) {
!function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}(i);
}
ES6 下能用 let
:
for(let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
也可以用 bind,瞬间逼格提升:
for(var i = 1; i <= 5; i++) {
setTimeout(console.log.bind(console, i), i * 1000);
}