JavaScript深入之bind的模擬實現(xiàn)

JavaScript深入之bind的模擬實現(xiàn)

bind() 方法會創(chuàng)建一個函數(shù)拜马,當這個新函數(shù)被調(diào)用的時候,bind() 的第一個參數(shù)將作為他運行時的this,之后的一序列參數(shù)將會在傳遞的參數(shù)前作為他的參數(shù)

其實這也可以看出bind函數(shù)的兩個特點

  • 返回一個新函數(shù)
  • 可以傳入?yún)?shù)

先看一個例子----返回函數(shù)的實現(xiàn)

var foo = {
    value : 2
}
function bar(){
    console.log(this.value)
}
var bindBar = bar.bind(foo)
bindBar();
// 2

我們定義的bind2 的函數(shù)要定義在Function.prototype,這是因為我們函數(shù)都是由Function創(chuàng)建的,window也是Function.prototype的實例

window.__proto__.__proto__.__proto__.__proto__.__proto__ == Object.prototype.__proto__
//true
Object.__proto__  == Function.prototype
//true

window.__proto__.__proto__.__proto__.__proto__ == Function.prototype.__proto__
//true
Function.prototype.bind2 = function(context){
    console.log(111, this)
    //這個this就指向調(diào)用者。如果是bar函數(shù)坟漱,就指向bar函數(shù)
    //context表示的是傳入的對象 也就是說
    //bar函數(shù)中的this指向context中的this
    
    var self = this
    return function(){
    self.apply(context)
    }
}

var foo = {
    value:3
}
function bar(){
    console.log(this.value)
}
var bindFoo = bar.bind2(foo);
//bind2 返回的只是bind2中的新函數(shù),所以需要再調(diào)用一次
console.log(bindFoo()) //3

傳參的模擬實現(xiàn)

先看騷騷的bind傳參,繼續(xù)剛才的騷例子

var foos = {
    value:4
}
function foo(name,age){
    console.log(name)
    console.log(age)
    console.log(this.value)
}

var bindFoo = foo.bind(foos, 'xiaolizi')
bindFoo(18)
// xiaolizi
//18
// 4

foo需要傳入name和age兩個參數(shù)更哄,而且可以在bind的時候芋齿,只傳一個name,然后再執(zhí)行返回的函數(shù)的時候再傳入另一個參數(shù)age
所以我們再第二版需要對arguments進行處理

第二版
Function.prototype.bind2 = function(context){
    var self = this;
//因為arguments是類數(shù)組對象成翩,不能使用slice觅捆,所以使用call改變this的指向
//從slice(1) 是截取第一個參數(shù)之后的參數(shù),因為第一個參數(shù)是傳入的對象context麻敌。
    var args = Array.prototype.slice.call(arguments,1)
    return function(){
    //這個是截取第二個調(diào)用的函數(shù)栅炒,所以截取全部的參數(shù)
        var bindArgs = Array.prototype.slice.call(arguments)
        return self.apply(context,args.concat(bindArgs))
        }
}

var foos = {
    value:4
}
function foo(name,age){
    console.log(name)
    console.log(age)
    console.log(this.value)
}

var bindFoo = foo.bind2(foos, 'xiaolizi')
bindFoo(18)

構造函數(shù)效果的模擬實現(xiàn)

一個綁定函數(shù)也能使用new操作符創(chuàng)建對象,這種行為就像把函數(shù)當做構造器术羔,提供的this值被忽略赢赊,同時調(diào)用時的參數(shù)被提供給模擬函數(shù)

其實也就是說bind返回的函數(shù)作為構造函數(shù)的時候,bind時指定的this值會失效级历,但傳入的參數(shù)依然有效释移。

var value = 2;

var foo = {
    value: 1
};

function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}

bar.prototype.friend = 'kevin';

var bindFoo = bar.bind(foo, 'daisy');

var obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin

注意:盡管在全局和 foo 中都聲明了 value 值,最后依然返回了 undefind寥殖,說明綁定的 this 失效了玩讳,如果大家了解 new 的模擬實現(xiàn),就會知道這個時候的 this 已經(jīng)指向了 obj嚼贡。

// 第三版
Function.prototype.bind2 = function (context) {
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        // 當作為構造函數(shù)時熏纯,this 指向?qū)嵗藭r結果為 true粤策,將綁定函數(shù)的 this 指向該實例樟澜,可以讓實例獲得來自綁定函數(shù)的值
        // 以上面的是 demo 為例,如果改成 `this instanceof fBound ? null : context`叮盘,實例只是一個空對象秩贰,將 null 改成 this ,實例會具有 habit 屬性
        // 當作為普通函數(shù)時熊户,this 指向 window萍膛,此時結果為 false,將綁定函數(shù)的 this 指向 context
        return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
    }
    // 修改返回函數(shù)的 prototype 為綁定函數(shù)的 prototype嚷堡,實例就可以繼承綁定函數(shù)的原型中的值
//這里實際吧fBound 作為function,也就是第一調(diào)用的時候就返回一個函數(shù),函數(shù)里面再根據(jù)調(diào)用的方式蝌戒,確定this的指向串塑,當?shù)诙沃赶虻臅r候,this指向的開始改變并賦值給fBound.prototype 就可以改變this的指向
    fBound.prototype = this.prototype;
    return fBound;
}

構造函數(shù)效果的優(yōu)化實現(xiàn)

但是在這個寫法中北苟,我們直接將 fBound.prototype = this.prototype桩匪,我們直接修改 fBound.prototype 的時候,也會直接修改綁定函數(shù)的 prototype友鼻。這個時候傻昙,我們可以通過一個空函數(shù)來進行中轉(zhuǎn):

// 第四版
Function.prototype.bind2 = function (context) {

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}

最終版

Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}

以上參考一下文章加了一些自己的理解

JavaScript深入之bind的模擬實現(xiàn)

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市彩扔,隨后出現(xiàn)的幾起案子妆档,更是在濱河造成了極大的恐慌,老刑警劉巖虫碉,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贾惦,死亡現(xiàn)場離奇詭異,居然都是意外死亡敦捧,警方通過查閱死者的電腦和手機须板,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兢卵,“玉大人习瑰,你說我怎么就攤上這事』嗷纾” “怎么了杰刽?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長王滤。 經(jīng)常有香客問我贺嫂,道長,這世上最難降的妖魔是什么雁乡? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任第喳,我火速辦了婚禮,結果婚禮上踱稍,老公的妹妹穿的比我還像新娘曲饱。我一直安慰自己,他們只是感情好珠月,可當我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布扩淀。 她就那樣靜靜地躺著,像睡著了一般啤挎。 火紅的嫁衣襯著肌膚如雪驻谆。 梳的紋絲不亂的頭發(fā)上卵凑,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天,我揣著相機與錄音胜臊,去河邊找鬼勺卢。 笑死,一個胖子當著我的面吹牛象对,可吹牛的內(nèi)容都是我干的黑忱。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼勒魔,長吁一口氣:“原來是場噩夢啊……” “哼甫煞!你這毒婦竟也來了?” 一聲冷哼從身側響起冠绢,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤抚吠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后唐全,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體埃跷,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年邮利,在試婚紗的時候發(fā)現(xiàn)自己被綠了弥雹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡延届,死狀恐怖剪勿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情方庭,我是刑警寧澤厕吉,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站械念,受9級特大地震影響头朱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜龄减,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一项钮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧希停,春花似錦烁巫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至违崇,卻和暖如春阿弃,著一層夾襖步出監(jiān)牢的瞬間诊霹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工恤浪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留畅哑,地道東北人肴楷。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓水由,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赛蔫。 傳聞我的和親對象是個殘疾皇子砂客,可洞房花燭夜當晚...
    茶點故事閱讀 45,747評論 2 361

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