**bind()** 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

thisArg
调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用 bindsetTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者thisArgnullundefined,执行作用域的 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 值会被忽略,但前置参数仍会提供给模拟函数。

创建绑定函数

bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 值。JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题:

  1. this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
  2. var module = {
  3. x: 81,
  4. getX: function() { return this.x; }
  5. };
  6. module.getX(); // 81
  7. var retrieveX = module.getX;
  8. retrieveX();
  9. // 返回 9 - 因为函数是在全局作用域中调用的
  10. // 创建一个新函数,把 'this' 绑定到 module 对象
  11. // 新手可能会将全局变量 x 与 module 的属性 x 混淆
  12. var boundGetX = retrieveX.bind(module);
  13. boundGetX(); // 81

偏函数

bind() 的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。

  1. function list() {
  2. return Array.prototype.slice.call(arguments);
  3. }
  4. function addArguments(arg1, arg2) {
  5. return arg1 + arg2
  6. }
  7. var list1 = list(1, 2, 3); // [1, 2, 3]
  8. var result1 = addArguments(1, 2); // 3
  9. // 创建一个函数,它拥有预设参数列表。
  10. var leadingThirtysevenList = list.bind(null, 37);
  11. // 创建一个函数,它拥有预设的第一个参数
  12. var addThirtySeven = addArguments.bind(null, 37);
  13. var list2 = leadingThirtysevenList();
  14. // [37]
  15. var list3 = leadingThirtysevenList(1, 2, 3);
  16. // [37, 1, 2, 3]
  17. var result2 = addThirtySeven(5);
  18. // 37 + 5 = 42
  19. var result3 = addThirtySeven(5, 10);
  20. // 37 + 5 = 42 ,第二个参数被忽略

配合setTImeout

在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或 global)对象。当类的方法中需要 this 指向类的实例时,你可能需要显式地把 this 绑定到回调函数,就不会丢失该实例的引用。

  1. function LateBloomer() {
  2. this.petalCount = Math.ceil(Math.random() * 12) + 1;
  3. }
  4. // 在 1 秒钟后声明 bloom
  5. LateBloomer.prototype.bloom = function() {
  6. window.setTimeout(this.declare.bind(this), 1000);
  7. };
  8. LateBloomer.prototype.declare = function() {
  9. console.log('I am a beautiful flower with ' +
  10. this.petalCount + ' petals!');
  11. };
  12. var flower = new LateBloomer();
  13. flower.bloom(); // 一秒钟后, 调用 'declare' 方法

注意:一旦函数通过bind绑定了有效的this对象,那么在函数执行过程中this会指向该对象,即使使用call、apply也不能改变this的指向
bind 进行深入的理解,我们来看一下 bind 的 polyfill 实现(ie6~ie8不支持该方法):

  1. if (!Function.prototype.bind) { //浏览器js中不支持bind方法情况下
  2. Function.prototype.bind = function(context) { //在函数原型对象自定义bind方法,context是this重指向的对象,也就是上面function(){}.bind(text)中的text对象
  3. var self = this //因为这个自定义的bind方法返回的是一个匿名函数,匿名函数具有全局性,其this会指向window。所以这里需要将bind方法触发时的调用对象this进行保存
  4. , args = Array.prototype.slice.call(arguments); //这里借用了数组原型对象的slice方法将形参 类数组转化为真正的数组
  5. return function() { //bind方法返回一个匿名函数
  6. return self.apply(context, args.slice(1)); //self就是函数触发时的调用对象,this重指向bind方法的第一个参数对象,
  7. }
  8. };
  9. }