成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript深入之call和apply的模擬實(shí)現(xiàn)

miya / 2560人閱讀

摘要:深入系列第十篇,通過(guò)和的模擬實(shí)現(xiàn),帶你揭開(kāi)和改變的真相一句話介紹方法在使用一個(gè)指定的值和若干個(gè)指定的參數(shù)值的前提下調(diào)用某個(gè)函數(shù)或方法。如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,?qǐng)務(wù)必給予指正,十分感謝。

JavaScript深入系列第十篇,通過(guò)call和apply的模擬實(shí)現(xiàn),帶你揭開(kāi)call和apply改變this的真相

call

一句話介紹 call:

call() 方法在使用一個(gè)指定的 this 值和若干個(gè)指定的參數(shù)值的前提下調(diào)用某個(gè)函數(shù)或方法。

舉個(gè)例子:

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call(foo); // 1

注意兩點(diǎn):

call 改變了 this 的指向,指向到 foo

bar 函數(shù)執(zhí)行了

模擬實(shí)現(xiàn)第一步

那么我們?cè)撛趺茨M實(shí)現(xiàn)這兩個(gè)效果呢?

試想當(dāng)調(diào)用 call 的時(shí)候,把 foo 對(duì)象改造成如下:

var foo = {
    value: 1,
    bar: function() {
        console.log(this.value)
    }
};

foo.bar(); // 1

這個(gè)時(shí)候 this 就指向了 foo,是不是很簡(jiǎn)單呢?

但是這樣卻給 foo 對(duì)象本身添加了一個(gè)屬性,這可不行吶!

不過(guò)也不用擔(dān)心,我們用 delete 再刪除它不就好了~

所以我們模擬的步驟可以分為:

將函數(shù)設(shè)為對(duì)象的屬性

執(zhí)行該函數(shù)

刪除該函數(shù)

以上個(gè)例子為例,就是:

// 第一步
foo.fn = bar
// 第二步
foo.fn()
// 第三步
delete foo.fn

fn 是對(duì)象的屬性名,反正最后也要?jiǎng)h除它,所以起成什么都無(wú)所謂。

根據(jù)這個(gè)思路,我們可以嘗試著去寫(xiě)第一版的 call2 函數(shù):

// 第一版
Function.prototype.call2 = function(context) {
    // 首先要獲取調(diào)用call的函數(shù),用this可以獲取
    context.fn = this;
    context.fn();
    delete context.fn;
}

// 測(cè)試一下
var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call2(foo); // 1

正好可以打印 1 哎!是不是很開(kāi)心!(~ ̄▽ ̄)~

模擬實(shí)現(xiàn)第二步

最一開(kāi)始也講了,call 函數(shù)還能給定參數(shù)執(zhí)行函數(shù)。舉個(gè)例子:

var foo = {
    value: 1
};

function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}

bar.call(foo, "kevin", 18);
// kevin
// 18
// 1

注意:傳入的參數(shù)并不確定,這可咋辦?

不急,我們可以從 Arguments 對(duì)象中取值,取出第二個(gè)到最后一個(gè)參數(shù),然后放到一個(gè)數(shù)組里。

比如這樣:

// 以上個(gè)例子為例,此時(shí)的arguments為:
// arguments = {
//      0: foo,
//      1: "kevin",
//      2: 18,
//      length: 3
// }
// 因?yàn)閍rguments是類數(shù)組對(duì)象,所以可以用for循環(huán)
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
    args.push("arguments[" + i + "]");
}

// 執(zhí)行后 args為 [foo, "kevin", 18]

不定長(zhǎng)的參數(shù)問(wèn)題解決了,我們接著要把這個(gè)參數(shù)數(shù)組放到要執(zhí)行的函數(shù)的參數(shù)里面去。

// 將數(shù)組里的元素作為多個(gè)參數(shù)放進(jìn)函數(shù)的形參里
context.fn(args.join(","))
// (O_o)??
// 這個(gè)方法肯定是不行的啦?。?!

也許有人想到用 ES6 的方法,不過(guò) call 是 ES3 的方法,我們?yōu)榱四M實(shí)現(xiàn)一個(gè) ES3 的方法,要用到ES6的方法,好像……,嗯,也可以啦。但是我們這次用 eval 方法拼成一個(gè)函數(shù),類似于這樣:

eval("context.fn(" + args +")")

這里 args 會(huì)自動(dòng)調(diào)用 Array.toString() 這個(gè)方法。

所以我們的第二版克服了兩個(gè)大問(wèn)題,代碼如下:

// 第二版
Function.prototype.call2 = function(context) {
    context.fn = this;
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push("arguments[" + i + "]");
    }
    eval("context.fn(" + args +")");
    delete context.fn;
}

// 測(cè)試一下
var foo = {
    value: 1
};

function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}

bar.call2(foo, "kevin", 18); 
// kevin
// 18
// 1

(??????)??

模擬實(shí)現(xiàn)第三步

模擬代碼已經(jīng)完成 80%,還有兩個(gè)小點(diǎn)要注意:

1.this 參數(shù)可以傳 null,當(dāng)為 null 的時(shí)候,視為指向 window

舉個(gè)例子:

var value = 1;

function bar() {
    console.log(this.value);
}

bar.call(null); // 1

雖然這個(gè)例子本身不使用 call,結(jié)果依然一樣。

2.函數(shù)是可以有返回值的!

舉個(gè)例子:

var obj = {
    value: 1
}

function bar(name, age) {
    return {
        value: this.value,
        name: name,
        age: age
    }
}

console.log(bar.call(obj, "kevin", 18));
// Object {
//    value: 1,
//    name: "kevin",
//    age: 18
// }

不過(guò)都很好解決,讓我們直接看第三版也就是最后一版的代碼:

// 第三版
Function.prototype.call2 = function (context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push("arguments[" + i + "]");
    }

    var result = eval("context.fn(" + args +")");

    delete context.fn
    return result;
}

// 測(cè)試一下
var value = 2;

var obj = {
    value: 1
}

function bar(name, age) {
    console.log(this.value);
    return {
        value: this.value,
        name: name,
        age: age
    }
}

bar.call(null); // 2

console.log(bar.call2(obj, "kevin", 18));
// 1
// Object {
//    value: 1,
//    name: "kevin",
//    age: 18
// }

到此,我們完成了 call 的模擬實(shí)現(xiàn),給自己一個(gè)贊 b( ̄▽ ̄)d

apply的模擬實(shí)現(xiàn)

apply 的實(shí)現(xiàn)跟 call 類似,在這里直接給代碼,代碼來(lái)自于知乎 @鄭航的實(shí)現(xiàn):

Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push("arr[" + i + "]");
        }
        result = eval("context.fn(" + args + ")")
    }

    delete context.fn
    return result;
}
下一篇文章

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

重要參考

知乎問(wèn)題 不能使用call、apply、bind,如何用 js 實(shí)現(xiàn) call 或者 apply 的功能?

深入系列

JavaScript深入系列目錄地址:https://github.com/mqyqingfeng/Blog。

JavaScript深入系列預(yù)計(jì)寫(xiě)十五篇左右,旨在幫大家捋順JavaScript底層知識(shí),重點(diǎn)講解如原型、作用域、執(zhí)行上下文、變量對(duì)象、this、閉包、按值傳遞、call、apply、bind、new、繼承等難點(diǎn)概念。

如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,?qǐng)務(wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎star,對(duì)作者也是一種鼓勵(lì)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91675.html

相關(guān)文章

  • JavaScript深入bind模擬實(shí)現(xiàn)

    摘要:也就是說(shuō)當(dāng)返回的函數(shù)作為構(gòu)造函數(shù)的時(shí)候,時(shí)指定的值會(huì)失效,但傳入的參數(shù)依然生效。構(gòu)造函數(shù)效果的優(yōu)化實(shí)現(xiàn)但是在這個(gè)寫(xiě)法中,我們直接將,我們直接修改的時(shí)候,也會(huì)直接修改函數(shù)的。 JavaScript深入系列第十一篇,通過(guò)bind函數(shù)的模擬實(shí)現(xiàn),帶大家真正了解bind的特性 bind 一句話介紹 bind: bind() 方法會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind() 的第一個(gè)參數(shù)...

    FingerLiu 評(píng)論0 收藏0
  • JavaScript深入new模擬實(shí)現(xiàn)

    摘要:深入系列第十二篇,通過(guò)的模擬實(shí)現(xiàn),帶大家揭開(kāi)使用獲得構(gòu)造函數(shù)實(shí)例的真相一句話介紹運(yùn)算符創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對(duì)象類型之一也許有點(diǎn)難懂,我們?cè)谀M之前,先看看實(shí)現(xiàn)了哪些功能。 JavaScript深入系列第十二篇,通過(guò)new的模擬實(shí)現(xiàn),帶大家揭開(kāi)使用new獲得構(gòu)造函數(shù)實(shí)例的真相 new 一句話介紹 new: new 運(yùn)算符創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具...

    tianlai 評(píng)論0 收藏0
  • JavaScript 深入 call apply 模擬實(shí)現(xiàn)

    摘要:第一版首先要獲取調(diào)用的函數(shù),用可以獲取的指向?yàn)?,因?yàn)槭堑膶?shí)例相當(dāng)于把掛載到上,所以可以取到測(cè)試一下但是第一版不可以傳遞多個(gè)參數(shù)第二版這里會(huì)自動(dòng)調(diào)用這個(gè)方法。 // 第一版 Function.prototype.call2 = function(context) { // 首先要獲取調(diào)用call的函數(shù),用this可以獲取 // this的指向?yàn)閎ar,因?yàn)閎ar是Func...

    邱勇 評(píng)論0 收藏0
  • JavaScript深入系列15篇正式完結(jié)!

    摘要:寫(xiě)在前面深入系列共計(jì)篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順底層知識(shí)的系列。深入系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫(xiě)在前面 JavaScript 深入系列共計(jì) 15 篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順 JavaScript 底層知識(shí)的系列。重點(diǎn)講解了如原型、作用域、執(zhí)行上下文、變量對(duì)象、this、...

    fxp 評(píng)論0 收藏0
  • 【進(jìn)階3-3期】深度解析 call apply 原理、使用場(chǎng)景及實(shí)現(xiàn)

    摘要:之前文章詳細(xì)介紹了的使用,不了解的查看進(jìn)階期。不同的引擎有不同的限制,核心限制在,有些引擎會(huì)拋出異常,有些不拋出異常但丟失多余參數(shù)。存儲(chǔ)的對(duì)象能動(dòng)態(tài)增多和減少,并且可以存儲(chǔ)任何值。這邊采用方法來(lái)實(shí)現(xiàn),拼成一個(gè)函數(shù)。 之前文章詳細(xì)介紹了 this 的使用,不了解的查看【進(jìn)階3-1期】。 call() 和 apply() call() 方法調(diào)用一個(gè)函數(shù), 其具有一個(gè)指定的 this 值和分...

    godlong_X 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

miya

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<