概念
function.bind(thisArg[, arg1[, arg2[, ...]]]) 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
-
thisArg- 调用绑定函数时作为
this参数传递给目标函数的值 - 如果使用
new运算符构造绑定函数,则忽略该值 - 当使用
bind在setTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object - 如果
bind函数的参数列表为空,或者thisArg是null或undefined,执行作用域的this将被视为新函数的thisArg
- 调用绑定函数时作为
-
arg1, arg2, ...当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
使用示例
创建绑定函数
this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
var module = {
x: 81,
getX: function () {
return this.x;
},
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX();
// 返回 9 - 因为函数是在全局作用域中调用的
// 创建一个新函数,把 'this' 绑定到 module 对象
// 新手可能会将全局变量 x 与 module 的属性 x 混淆
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81
偏函数
function list() {
return Array.prototype.slice.call(arguments);
}
function addArguments(arg1, arg2) {
return arg1 + arg2;
}
var list1 = list(1, 2, 3); // [1, 2, 3]
var result1 = addArguments(1, 2); // 3
// 创建一个函数,它拥有预设参数列表。
var leadingThirtysevenList = list.bind(null, 37);
// 创建一个函数,它拥有预设的第一个参数
var addThirtySeven = addArguments.bind(null, 37);
var list2 = leadingThirtysevenList();
// [37]
var list3 = leadingThirtysevenList(1, 2, 3);
// [37, 1, 2, 3]
var result2 = addThirtySeven(5);
// 37 + 5 = 42
var result3 = addThirtySeven(5, 10);
// 37 + 5 = 42,第二个参数被忽略
配合 setTimeout
在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window(或 global)对象。当类的方法中需要 this 指向类的实例时,你可能需要显式地把 this 绑定到回调函数,就不会丢失该实例的引用。
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// 在 1 秒钟后声明 bloom
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(); // 一秒钟后,调用 'declare' 方法
作为构造函数使用的绑定函数
- 不应该用在任何生产环境中
- 当一个绑定函数是用来构建一个值的,原来提供的
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'
var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0 /*x*/);
// 本页下方的 polyfill 不支持运行这行代码,
// 但使用原生的 bind 方法运行是没问题的:
var YAxisPoint = Point.bind(null, 0 /*x*/);
/*(译注:polyfill 的 bind 方法中,如果把 bind 的第一个参数加上,
即对新绑定的 this 执行 Object(this),包装为对象,
因为 Object(null) 是 {},所以也可以支持)*/
var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'
axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new YAxisPoint(17, 42) instanceof Point; // true
快捷调用
在你想要为一个需要特定的 this 值的函数创建一个捷径(shortcut)的时候,bind() 也很好用。
var slice = Array.prototype.slice;
slice.apply(arguments);
// 相同
var slice = Function.prototype.apply.bind(slice);
slice(arguments);
模拟
Function.prototype.myBind = function (context) {
if (typeof this !== "function") {
throw new Error(
"Function.prototype.bind - what is trying to be bound is not callable"
);
}
var _this = this;
// args 是 bind 函数传递的参数
var args = Array.prototype.slice.call(arguments, 1);
return function F() {
// bindArgs 是 F 函数的参数
var bindArgs = Array.prototype.slice.call(arguments);
if (this instanceof F) {
var t = [];
for (var i = 0, len1 = args.length; i < len1; i++) {
t.push("args[" + i + "]");
}
for (var j = 0, len2 = arguments.length; j < len2; j++) {
t.push("arguments[" + j + "]");
}
return eval("new _this(" + t + ")");
}
return _this.apply(context, args.concat(bindArgs));
};
};
Function.prototype.myBind = function (context) {
if (typeof this !== "function") {
console.error(
"Function.prototype.bind - what is trying to be bound is not callable"
);
}
const _this = this;
// args 是 bind 函数传递的参数
const args = [...arguments].slice(1);
return function F() {
if (this instanceof F) {
// arguments 是 F 函数的参数
return new _this(...args, ...arguments);
}
return _this.apply(context, [...args, ...arguments]);
};
};