javascript - js中call方法的實現
PHP中文网
PHP中文网 2017-06-12 09:30:07
0
3
841
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;
}

是為了模擬call的實現,請問為什麼要push一個字串,下面再用eval?直接傳入arguments[i],然後下面用context.fn(args)為什麼不行?

PHP中文网
PHP中文网

认证高级PHP讲师

全部回覆(3)
给我你的怀抱

這裡我相信你也已經明白了call的原理,這裡我簡要還是說明一下原理,我也是參考JavaScript權威指南的說明然後用代碼實現的。

首先我們來看看call的語法和定義,來自ECMAScript規範中文版:

還是簡單舉個栗子:

var jawil = {
    name: "jawil",
    sayHello: function (age) {
         console.log("hello, i am ", this.name + " " + age + " years old");
     }
};

var  lulin = {
    name: "lulin",
};

jawil.sayHello(24);

// hello, i am jawil 24 years old

然後看看使用call之後的輸出:

jawil.sayHello.call(lulin, 24);// hello, i am lulin 24 years old

下面我結合栗子來解答你的疑問:

是為了模擬call的實現,請問為什麼要push一個字串,下面再用eval?直接傳入arguments[i],然後下面用context.fn(args)為什麼不行?

首先你要明白上面模擬的函數對應栗子中變數的關係:

context  => lulin

context.fn => jawil.sayHello

注意到這一步,我們只是把 jawil.sayHello的引用地址給了lulin.sayHello

原來 jawil.sayHello.call(context,arg1,arg2,arg3,arg4)

照你的方式剔除context得到args=[arg1,arg2,arg3,arg4]

然後執行lulin.sayHello([arg1,arg2,arg3,arg4])哈哈,很有迷惑性是不是,看著沒問題,其實已經變味了,原來是原來是四個參數,現在集合到一個陣列就是一個陣列參數了,問題就出在這裡。

那麼怎麼解決這個問題,思路就是上面那樣,把所有參數拼成字串,然後用eval執行。

我們想要的效果是lulin.sayHello(arg1,arg2,arg3,arg4)這樣的,因為lulin.sayHello要重組參數,你不能拿到一個參數執行一次函數把吧,或把參數存到一起一次執行吧,唯一的想到的做法就是把所有參數拼成字串,然後用eval執行,

類似這種:「lulin.sayHello(arg1,arg2,arg3,arg4)」這才是我們想要的方式,而不是lulin.sayHello([arg1,arg2,arg3,arg4]),也不是 lulin.sayHello(arg1),lulin.sayHello(arg2)...

什麼是eval,這裡也簡單說一下,我就當你什麼都不知道。

先簡單了解一下eval函數吧
定義和用法

eval() 函數可計算某個字串,並執行其中的 JavaScript 程式碼。

語法:
eval(string)

string必需。要計算的字串,其中包含要計算的 JavaScript 表達式或要執行的語句。此方法只接受原始字串作為參數,如果 string 參數不是原始字串,那麼該方法將不作任何改變地傳回。因此請不要為 eval() 函數傳遞 String 物件作為參數。

簡單來說吧,就是用JavaScript的解析引擎來解析這一堆字串裡面的內容,這麼說吧,你可以這麼理解,你把eval看成是標籤。

eval('function Test(a,b,c,d){console.log(a,b,c,d)};
Test(1,2,3,4)')

就是相當於這樣

<script>
function Test(a,b,c,d){
console.log(a,b,c,d)
};
Test(1,2,3,4)
</script>

好了,我們再來看上面那段程式碼,其實還有坑的,來看看調式直式觀點。下面是完整的調式程式碼:

Function.prototype.call2 = function(context) {
    context.fn = this;
    var args = [];
    for (var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }

    console.log(args)

    var star = 'context.fn(' + args + ')'

    console.log(star)
    
    eval('context.fn(' + args + ')');

    delete context.fn;
}


var jawil = {
    name: "jawil",
    sayHello: function(age) {
        console.log("hello, i am ", this.name + " " + age + " years old");
    }
};

var lulin = {
    name: "lulin",
};


jawil.sayHello.call2(lulin, 24, 25);

看args的輸出:

["arguments[1]", "arguments[2]"]

然後再看 'context.fn(' + args + ')'的輸出:

"context.fn(arguments[1],arguments[2])"是不是有點懵逼

其實這裡涉及到了隱式轉換,舉個栗子:

'jawil'+[1,2]+[3,4]+3等於多少?
等於"jawil1,23,43"
其實這個相當於'jawil'+[1,2].toString()+[3,4].toString()+3

篇幅有限,更多隱式轉換,請參考我的這篇文章:從++[[]][+[]]+[+[]]==10?深入淺出弱類型JS的隱式轉換

說到這裡,我把核心的都說了,你自己好好領悟領悟,原作者寫這個東西估計也是參看別人的,很多東西沒講清楚,估計由於篇幅有限,所以一筆帶過,看似很短的一段程式碼,其實包含了不少知識點的。

曾经蜡笔没有小新

args 是一個數組,context.fn(args) 只有一個參數,正常情況下可以用 apply 可以解決數組轉參數,但這裡模擬 call ,用 apply 就沒意思了。所以它利用了陣列的 toString() 把 context 以外的參數搬到 context.fn 上。

淡淡烟草味

因為 arguments[0] 是 context
你沒看循環變數從1開始的麼!

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板