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

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