JavaScript bind() 方法的实现

bind() 方法的特点

  • bind() 方法会创建一个新函数。
  • 当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,
  • 可以把除了第一个参数以外的其他参数都传递给下层的函数(这种技术称为“部分应用”,是“柯里化”的一种)注①
  • 如果 bind() 返回的函数 作为构造函数使用,bind 时指定的 this 值会失效,但传入的参数有效。
    • new 调用返回的实例能够继承 绑定函数的原型 中的值

注①:

  • 来自《你不知道的JavaScript》
  • 关于局部应用与柯里化,引用 mqyqingfeng:

    • 柯里化是将一个多参数函数转换成多个单参数函数,也就是将一个 n 元函数转换成 n 个一元函数。
    • 局部应用则是固定一个函数的一个或者多个参数,也就是将一个 n 元函数转换成一个 n - x 元函数。
    • 如果说两者有什么关系的话,引用 functional-programming-jargon 中的描述就是:Curried functions are automatically partially applied.

bind() 实现

方法一

Function.prototype.fakeBind = function(context) {
  if (typeof this !== "function") {
    throw new Error("Bind must be called on a function");
  }
  let self = this;
  // 获得第一个参数以外的其他参数
  let args = Array.prototype.slice.call(arguments, 1);

  let inner = function() {
    // 获得返回函数执行时被传入的参数
    let innerArgs = Array.prototype.slice.call(arguments);
    // 1 new 情况下,this 指向实例。此时 bind 时指定的 this 值应予以失效;
    // 2 实例 instanceof 构造函数 返回值 true、false,确定是否为 new 调用;
    // 3 匿名函数直接调用 this 指向全局对象。此时应予以修改 this 的绑定
    return self.apply(
      this instanceof inner ? 
      this : 
      context, args.concat(innerArgs)
    );
  };
  // inner.prototype = this.prototype 
  // 按上面这么写的话,修改 返回函数原型对象(inner.prototype)的同时把 绑定函数的原型对象(this.prototype)也同时修改了。
  // 用匿名函数做中转,this.protptype 就安全了。画个原型链的图就清楚了。
  //注②
  let fNOP = function() {};
  fNOP.prototype = this.prototype;
  inner.prototype = new fNOP();
  return inner;
};

注②

fNOP.prototype = this.prototype; 就是将 this.prototype 原型对象作为 fNOP.prototype 的原型对象,也就是 this.prototype 和 fNOP.prototype 指向同一个对象。

像 var f = new fNOP(); 之后找原型链上的属性,就是通过 f.proto

因为 f.proto == fNOP.prototype == this.prototype

就会去 this.prototype 上找属性了。

方法二 ES6版

Function.prototype.fakeBindES6 = function(context, ...rest) {
  if (typeof this !== "function") {
    throw new Error("Bind must be called on a function");
  }
  var self = this;
  return function inner(...args) {
    if (this instanceof inner) {
      // 当返回的内层函数作为构造函数使用,bind 时绑定的 this 失效。
      // 即此处直接执行绑定函数,而不使用 apply 对 this 进行绑定 
      return new self(...rest, ...args);
    }
    // 当作为普通函数调用,this 指向传入的对象
    return self.apply(context, rest.concat(args));
  };
};

参考:https://github.com/mqyqingfeng/Blog/issues/12

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章