實現(xiàn) Function.prototype.bind 的 Polyfill

最近的面試中被問到了 bind 這個方法简识,并寫出其 Polyfill,感覺回答的不太好感猛,在此重新整理一下七扰。
Function.prototype.bind 是 ES5 引入的方法,會返回一個新函數(shù)并修改函數(shù)的 this 指向陪白,舉個例子(摘自 MDN):

this.x = 9; 
let module = {
  x: 81,
  getX() {
    return this.x; 
  }
};

module.getX();  // 返回81颈走,this指向module

let retrieveX = module.getX;
retrieveX();  // 返回9,this指向全局作用域

// 創(chuàng)建一個新函數(shù)咱士,將this綁定到module對象
let boundGetX = retrieveX.bind(module);
boundGetX();  // 返回81立由,this指向module

所以,可以容易得出:

Function.prototype.bind = function (thisArg) {
  var self = this;
  return function() {
    return self.apply(thisArg, arguments);
  }
}

注意到 bind 方法的定義如下:

fun.bind(thisArg[, arg1[, arg2[, ...]]])
參數(shù)
thisArg
當(dāng)綁定函數(shù)被調(diào)用時序厉,該參數(shù)會作為原函數(shù)運行時的this指向锐膜。當(dāng)使用new操作符調(diào)用綁定函數(shù)時,該參數(shù)無效脂矫。
arg1, arg2, ...
當(dāng)綁定函數(shù)被調(diào)用時,這些參數(shù)將置于實參之前傳遞給被綁定的方法霉晕。

因此庭再,bind 除了可以綁定 this 指向外捞奕,還可以綁定調(diào)用參數(shù)。代碼修改如下:

Function.prototype.bind = function (thisArg) {
  // 借用 Array 的 slice 方法去掉第一個參數(shù) thisArg拄轻,剩下的是函數(shù)調(diào)用參數(shù)
  var bindArgs = Array.prototype.slice.call(arguments, 1);
  var self = this;
  return function() {
    return self.apply(thisArg, bindArgs.concat(arguments));
  }
}

此外颅围,還需要考慮到綁定的函數(shù)可以是構(gòu)造函數(shù),然而返回的新函數(shù)不具有綁定函數(shù)的原型鏈恨搓,因而需要修復(fù)原型鏈院促。代碼修改如下:

Function.prototype.bind = function (thisArg) {
  // 借用 Array 的 slice 方法去掉第一個參數(shù) thisArg,剩下的是函數(shù)調(diào)用參數(shù)
  var bindArgs = Array.prototype.slice.call(arguments, 1);
  var self = this;
  var bindFunction = function() {
    return self.apply(thisArg, bindArgs.concat(arguments));
  }
  // 引入空函數(shù) F斧抱,避免原型鏈上引用類型屬性共享
  function F() {}
  F.prototype = this.prototype;
  bindFunction.prototype = new F();
  // 修復(fù) constructor 屬性
  bindFunction.prototype.constructor = this;
  return bindFunction;
}

至此完成了 Function.prototype.bind 的 Polyfill常拓。
附 MDN 提供的 Polyfill,完善了邊界和異常:

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 // 獲取調(diào)用時(fBound)的傳參辉浦,bind 返回的函數(shù)入?yún)⑼沁@么傳遞的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 維護(hù)原型關(guān)系
    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    fBound.prototype = new fNOP();

    return fBound;
  };
}

仍然存在的問題:

  1. 這部分實現(xiàn)依賴于 Array.prototype.slice()弄抬,Array.prototype.concat()Function.prototype.call() 這些原生方法宪郊。
  2. 這部分實現(xiàn)創(chuàng)建的函數(shù)的實現(xiàn)并沒有 caller 以及會在 get掂恕,set 或者 deletion 上拋出 TypeError 錯誤的 arguments 屬性這兩個不可改變的“毒藥” 。(假如環(huán)境支持 Object.defineProperty弛槐,或者實現(xiàn)支持 __defineGetter____defineSetter__ 擴展)
  3. 這部分實現(xiàn)創(chuàng)建的函數(shù)有 prototype 屬性懊亡。(正確的綁定函數(shù)沒有的)
  4. 這部分實現(xiàn)創(chuàng)建的綁定函數(shù)所有的 length 屬性并不是同 ECMA-262 標(biāo)準(zhǔn)一致的:它的 length 是 0,而在實際的實現(xiàn)中根據(jù)目標(biāo)函數(shù)的 length 和預(yù)先指定的參數(shù)個數(shù)可能會返回非零的 length乎串。

Reference

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末店枣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子灌闺,更是在濱河造成了極大的恐慌艰争,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桂对,死亡現(xiàn)場離奇詭異甩卓,居然都是意外死亡,警方通過查閱死者的電腦和手機蕉斜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門逾柿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宅此,你說我怎么就攤上這事机错。” “怎么了父腕?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵弱匪,是天一觀的道長。 經(jīng)常有香客問我璧亮,道長萧诫,這世上最難降的妖魔是什么斥难? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮帘饶,結(jié)果婚禮上哑诊,老公的妹妹穿的比我還像新娘。我一直安慰自己及刻,他們只是感情好镀裤,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缴饭,像睡著了一般暑劝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茴扁,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天铃岔,我揣著相機與錄音,去河邊找鬼峭火。 笑死毁习,一個胖子當(dāng)著我的面吹牛讲衫,可吹牛的內(nèi)容都是我干的寝凌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妻往,長吁一口氣:“原來是場噩夢啊……” “哼稍浆!你這毒婦竟也來了载碌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤衅枫,失蹤者是張志新(化名)和其女友劉穎嫁艇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弦撩,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡步咪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了益楼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猾漫。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖感凤,靈堂內(nèi)的尸體忽然破棺而出悯周,到底是詐尸還是另有隱情,我是刑警寧澤陪竿,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布禽翼,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏闰挡。R本人自食惡果不足惜仇矾,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望解总。 院中可真熱鬧,春花似錦姐仅、人聲如沸花枫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劳翰。三九已至,卻和暖如春馒疹,著一層夾襖步出監(jiān)牢的瞬間佳簸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工颖变, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留生均,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓腥刹,卻偏偏與公主長得像马胧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子衔峰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容