JavaScript中的Call杜恰、Apply蚊逢、Bind的實(shí)現(xiàn)

我們知道在javascript中call和apply以及bind都可以改變this指向,那么它們是怎么實(shí)現(xiàn)的呢箫章?彼此之間有什么區(qū)別呢?首先我們先來分別解析一下它們,這篇文章簡單的介紹了實(shí)現(xiàn)call() , apply() , bind()的思路

JavaScript中的Call镜会、Apply檬寂、Bind的實(shí)現(xiàn)

call的解讀與實(shí)現(xiàn)

    var leo = {
        name: 'Leo',
        sayHai: function(){
            return "Hi I'm " + this.name;
        },
        add: function(a, b){
            console.log(this)
            return a + b;
        }
    }

    var neil = {
        name:'Neil'
    }

    console.log(leo.sayHai.call(neil)) //Neil
    console.log(leo.sayHai.call(null)) //undefine

如上面輸出結(jié)果可以看出:
1.call方法的第一個參數(shù)用于改變this指向,但是如果傳入null/undefined值戳表,this會指向window
2.call方法需要把實(shí)參按照形參的個數(shù)傳進(jìn)去
現(xiàn)在我們已經(jīng)知道如何使用call以及它的規(guī)則桶至,那么我們來封裝一個自己的call方法

    //ES3實(shí)現(xiàn)方法利用了eval()函數(shù),將字符串當(dāng)做JavaScript表達(dá)是執(zhí)行
    Function.prototype.es3call = function(ctx) {
        var ctx = ctx || global || window;
        ctx.fn = this;
        var args = [];
        for (var i = 1; i < arguments.length; i++) {
            args.push('arguments[' + i + ']');
        }
        var result = eval('ctx.fn(' + args.join(',') + ')');
        delete ctx.fn;
        return result;
    }
    
    //ES6實(shí)現(xiàn)方法利用了擴(kuò)展運(yùn)算符rest運(yùn)算符
    Function.prototype.es6call = function(ctx){
        var ctx = ctx || window || global;
        ctx.fn = this;
        var args = [];
        for(var i = 1; i < arguments.length; i++){
            args.push('arguments[' + i +']');
        }
        const result = ctx.fn(...args);
         return result;
    }

    console.log(leo.sayHai.es3call(neil)) //Neil
    console.log(leo.sayHai.es3call(null)) //undefine
    console.log(leo.sayHai.es6call(neil)) //Neil
    console.log(leo.sayHai.es6call(null)) //undefine

apply的解讀與實(shí)現(xiàn)

    console.log(leo.add.apply(neil, [2, 3])) //neil 5
    console.log(leo.add.apply(null, [2, 3])) //window or global 5

1.apply方法的第一個參數(shù)用于改變this指向匾旭,但是如果傳入null/undefined值镣屹,this會指向window
2.apply方法的第二個參數(shù)需要傳入一個實(shí)參列表,也就是arguments

    //ES3實(shí)現(xiàn)方法利用了eval()函數(shù)价涝,將字符串當(dāng)做JavaScript表達(dá)是執(zhí)行
    Function.prototype.es3apply = function(ctx, arr){
        var ctx = ctx || global || window;
        ctx.fn = this;
        var result = null;
        if(!arr){
            result = ctx.fn();
        }else{
            if(!(arr instanceof Array)) throw new Error('params must Array');
            var args = [];
            for(var i = 0; i < arr.length; i++){
                args.push('arr[' + i +']');
            }
            result = eval('ctx.fn('+ args.join(',') + ')');
          }
        delete ctx.fn;
        return result;
    }

    //ES6實(shí)現(xiàn)方法利用了擴(kuò)展運(yùn)算符rest運(yùn)算符
    Function.prototype.es6apply = function(ctx, arr){
        var ctx = ctx || global || window;
        ctx.fn = this;
        var result = null;
        if(!arr){
            result = ctx.fn();
        }else{
            if(!(arr instanceof Array)) throw new Error('params must Array');
            var args = [];
            for(var i = 0; i < arr.length; i++){
                args.push('arr[' + i +']');
            }
            result = ctx.fn(...args);
        }
        delete ctx.fn;
        return result;
    }
    console.log(leo.add.es3apply(neil, [2, 3])) //neil 5
    console.log(leo.add.es3apply(null, [2, 3])) //window or global 5
    console.log(leo.add.es6apply(neil, [2, 3])) //neil 5
    console.log(leo.add.es6apply(null, [2, 3])) //window or global 5

bind的解讀與實(shí)現(xiàn)

    var value = 'window';
    var obj = {
        value: 'obj'
    };
    Parent.prototype.value = 'parent';
    function Parent(){};
    Child.prototype = new Parent();
    function Child(){};
    function show(name){
        console.log(this.value, name)
    }

    show();  //window undefined
    var newShow1 = show.bind(obj);
    newShow1();//obj undefined
    var newShow2 = show.bind(null);
    newShow2();//window undefined
    var newShow3 = show.bind(obj, 'test');  //函數(shù)擁有預(yù)設(shè)的初始參數(shù)
    newShow3();//obj test
    new newShow3();//undefined test
    var oS = Child.bind(obj);
    var fn = new oS();
    console.log(fn, fn.value);    //Child{} "parent"  當(dāng)使用new 操作符調(diào)用綁定函數(shù)時(shí)女蜈,參數(shù)obj無效。

根據(jù)上面的運(yùn)行結(jié)果,我們可以總結(jié)一下bind的用法規(guī)則:
1.bind() 函數(shù)會創(chuàng)建一個新函數(shù)(稱為綁定函數(shù))伪窖,新函數(shù)與被調(diào)函數(shù)(綁定函數(shù)的目標(biāo)函數(shù))具有相同的函數(shù)體
2.bind方法的第一個參數(shù)也是用于改變this指向逸寓,如果傳入null/undefined,this會指向window
3.一個綁定函數(shù)也能使用new操作符創(chuàng)建對象:這種行為就像把原函數(shù)當(dāng)成構(gòu)造器覆山。提供的 this 值被忽略
4.bind方法可以使函數(shù)擁有預(yù)設(shè)的初始參數(shù)竹伸。這些參數(shù)(如果有的話)作為bind()的第二個參數(shù)跟在this(或其他對象)后面,之后它們會被插入到目標(biāo)函數(shù)的參數(shù)列表的開始位置簇宽,傳遞給綁定函數(shù)的參數(shù)會跟在它們的后面勋篓。

    //ES3實(shí)現(xiàn)方法
    Function.prototype.es3bind = function(ctx){
        var ctx = ctx || global || window;
        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
        if(typeof this !== "function") throw new Error('what is trying to be bind is not callback');
        var temp = function(){};
        var fn = function(){
        var fnArgs = Array.prototype.slice.call(arguments, 0);
            return self.apply(this instanceof fn ? this : ctx, args.concat(fnArgs));
        }
        // 先讓 temp 的原型方法指向 _this 即原函數(shù)的原型方法,繼承 _this 的屬性
        temp.prototype = this.prototype;
        // 再將 fn 即要返回的新函數(shù)的原型方法指向 temp 的實(shí)例化對象
        // 這樣魏割,既能讓 fn 繼承 _this 的屬性譬嚣,在修改其原型鏈時(shí),又不會影響到 _this 的原型鏈
        fn.prototype = new temp();     //原型繼承
        return fn;
    }
    
    //ES6實(shí)現(xiàn)方法
    Function.prototype.es6bind = function(ctx, ...rest){
        if(typeof this !== "function") throw new Error('what is trying to be bind is not callback');
        var self = this;
        return function F(...args){
            if(this instanceof F){
                return new self(...rest, ...args);
            }
            return self.apply(ctx, rest.concat(args));
        }
    }
    show();  //window undefined
    var newShow1 = show.es3bind(obj);
    newShow1();//obj undefined
    var newShow2 = show.es3bind(null);
    newShow2();//window undefined
    var newShow3 = show.es3bind(obj, 'test');  //函數(shù)擁有預(yù)設(shè)的初始參數(shù)
    newShow3();//obj test
    new newShow3();//undefined test
    var oS = Child.es3bind(obj);
    var fn = new oS();
    console.log(fn, fn.value);    //Child{} "parent"  當(dāng)使用new 操作符調(diào)用綁定函數(shù)時(shí)见妒,參數(shù)obj無效孤荣。

    show();  //window undefined
    var newShow1 = show.es6bind(obj);
    newShow1();//obj undefined
    var newShow2 = show.es6bind(null);
    newShow2();//window undefined
    var newShow3 = show.es6bind(obj, 'test');  //函數(shù)擁有預(yù)設(shè)的初始參數(shù)
    newShow3();//obj test
    new newShow3();//undefined test
    var oS = Child.es6bind(obj);
    var fn = new oS();
    console.log(fn, fn.value);    //Child{} "parent"  當(dāng)使用new 操作符調(diào)用綁定函數(shù)時(shí),參數(shù)obj無效须揣。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盐股,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子耻卡,更是在濱河造成了極大的恐慌疯汁,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卵酪,死亡現(xiàn)場離奇詭異幌蚊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)溃卡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門溢豆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瘸羡,你說我怎么就攤上這事漩仙。” “怎么了犹赖?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵队他,是天一觀的道長。 經(jīng)常有香客問我峻村,道長麸折,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任粘昨,我火速辦了婚禮垢啼,結(jié)果婚禮上窜锯,老公的妹妹穿的比我還像新娘。我一直安慰自己膊夹,他們只是感情好衬浑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著放刨,像睡著了一般工秩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上进统,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天助币,我揣著相機(jī)與錄音,去河邊找鬼螟碎。 笑死眉菱,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掉分。 我是一名探鬼主播俭缓,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼酥郭!你這毒婦竟也來了华坦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤不从,失蹤者是張志新(化名)和其女友劉穎惜姐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體椿息,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡歹袁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寝优。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片条舔。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖乏矾,靈堂內(nèi)的尸體忽然破棺而出孟抗,到底是詐尸還是另有隱情,我是刑警寧澤妻熊,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站仑最,受9級特大地震影響扔役,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜警医,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一亿胸、第九天 我趴在偏房一處隱蔽的房頂上張望坯钦。 院中可真熱鬧,春花似錦侈玄、人聲如沸婉刀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽突颊。三九已至,卻和暖如春潘悼,著一層夾襖步出監(jiān)牢的瞬間律秃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工治唤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棒动,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓宾添,卻偏偏與公主長得像船惨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缕陕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

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

  • 2018年3月14日粱锐,73班第三組戰(zhàn)友踐行情況總結(jié)如下: 三組戰(zhàn)友共11人 志愿者:李聘聘 1、三組戰(zhàn)友踐行基本情...
    翁翁yeah閱讀 148評論 0 1
  • 再說閉包:首先要理解函數(shù)的作用域(全局和私有)榄檬,內(nèi)部的可以訪問全局的卜范,全局的不可以訪問(內(nèi)容)私有的。函數(shù)執(zhí)行完畢...
    五四青年_4e7d閱讀 198評論 0 0
  • 1鹿榜、翻云覆雨:玩弄手段海雪,反復(fù)無常。 2舱殿、求全責(zé)備:對人對事要求十全十美奥裸。這里的“責(zé)備”是“要求完備”的意思,不是批...
    半夏風(fēng)吹閱讀 305評論 0 2
  • 一輪皎潔出水面沪袭, 煙波霧渺誰蕩舟湾宙。 燈影湖波醉兩岸, 相悅不知是夜半冈绊。 2019年3月4日 大李興業(yè)二十八年正月 ...
    香雪風(fēng)輕揚(yáng)閱讀 309評論 0 8