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)為什麼不行?
這裡我相信你也已經明白了
call
的原理,這裡我簡要還是說明一下原理,我也是參考JavaScript
權威指南的說明然後用代碼實現的。首先我們來看看
call
的語法和定義,來自ECMAScript規範中文版:還是簡單舉個栗子:
然後看看使用
call
之後的輸出:下面我結合栗子來解答你的疑問:
首先你要明白上面模擬的函數對應栗子中變數的關係:
注意到這一步,我們只是把
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(string)
簡單來說吧,就是用JavaScript的解析引擎來解析這一堆字串裡面的內容,這麼說吧,你可以這麼理解,你把
eval
看成是標籤。
就是相當於這樣
好了,我們再來看上面那段程式碼,其實還有坑的,來看看調式直式觀點。下面是完整的調式程式碼:
看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開始的麼!