bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
语法
function.bind(thisArg[, arg1[, arg2[, ...]]])
参数
- thisArg - 调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用 new 运算符构造绑定函数,则忽略该值。当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者 thisArg 是 null 或 undefined,执行作用域的 this 将被视为新函数的 thisArg。
- arg1, arg2, ... - 当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
返回值
返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。
描述
bind() 函数会创建一个新的绑定函数(bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。
绑定函数具有以下内部属性:
- [[BoundTargetFunction]] - 包装的函数对象
- [[BoundThis]] - 在调用包装函数时始终作为 this 值传递的值。
- [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
- [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个 this 值和一个包含通过调用表达式传递给函数的参数的列表。
当调用绑定函数时,它调用 [[BoundTargetFunction]] 上的内部方法 [[Call]],就像这样 Call(boundThis, args)。其中,boundThis 是 [[BoundThis]],args 是 [[BoundArguments]] 加上通过函数调用传入的参数列表。
绑定函数也可以使用 new 运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的 this 值会被忽略,但前置参数仍会提供给模拟函数。
Function.prototype.myBind
Function.prototype.myBind = function () { // 将类数组转为数组 var args = Array.prototype.slice.call(arguments); // 在参数数组中取出首个参数,也就是 context,现在参数数组中都是入参了 var context = args.splice(0, 1)[0]; // 保存this var fn = this; // 判断是否使用了 new 操作符, 如果用了 new 第一个参数就无效了 var Fn = function () {}; // 结果方法,最后会返回出去 var res = function res() { // 这里的 arguments 其实是调用时的入参 let rest = Array.prototype.slice.call(arguments); // this只和运行的时候有关系,所以这里的this和上面的fn不是一码事 // new res() 和 res()在调用的时候,res中的this是不同的东西 return fn.apply(this instanceof Fn ? this : context, args.concat(rest)); }; // 当使用了 new 关键字后,原型链的继承关系需要调整 if (this.prototype) { Fn.prototype = this.prototype; } res.prototype = new Fn(); // 将构造函数设置为自身,因为一个构造函数原型的构造函数指向构造函数本身 res.prototype.constructor = res; return res; };
测试
- 修改 this 指向
function fn() { console.log(this); } var a = fn.myBind({ name: "Frank" }); a(); // {name: "Frank"} fn(); // window
- bind 允许传递多个参数
function fn() { console.log(this, arguments); } var a = fn.myBind({ name: "Frank" }, "a", [1, 2, 3], 2); a(); // {name: "Frank"} Arguments(3) ["a", Array(3), 2, callee: ƒ, Symbol(Symbol.iterator): ƒ] fn(); // window Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
- bind 返回的方法也允许传入多个参数
function fn() { console.log(this, arguments); } var a = fn.myBind(null, "a", [1, 2, 3], 2); a(1, 2, 3, 4); // window Arguments(7) ["a", Array(3), 2, 1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ] fn(1, 2, 3, 4); // window Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
- 对 bind 返回的方法使用 new 关键字,生成的新对象和 调用 bind 传入的上下文无关
function fn() { this.name = "frank"; } var a = fn.myBind({ name: "json" }); var b = new a(); console.log(b); // res {name: "frank"} // name: "frank" // __proto__: fn
附原型链
function fn() { this.name = "frank"; } var a = new (fn.myBind({ name: "json" }))(); a.__proto__.constructor; // ƒ res() {...} var b = new fn(); b.__proto__.constructor = fn; fn.prototype.constructor = fn; b.__proto__ === fn.prototype;