理解JavaScript Call()函式原理。
最近在做面試題的過程中偶然碰到關於call函式的問題。然後再百度上查了查。偶然看到一篇文章: ofollow,noindex" target="_blank">JavaScript中的call、apply、bind深入理解 拋開其對call函式基本概念的介紹還有其他原理的介紹。其中一段函式吸引了我。
function fn1(){ console.log(1); } function fn2(){ console.log(2); } fn1.call(fn2);//輸出 1 fn1.call.call(fn2);//輸出 2
對於 fn1.call(fn2);我能夠理解,這段程式碼僅僅 使得 fn1物件的this指向了fn2;但是最終不影響fn1函式的執行。因為fn1中不包含對this的操作。不過 fn1.call.call(fn2);實在是令我費解。我一時半會沒有領會筆者的表達方式。花了很長時間去領會。最終還是看其他大神的部落格才得以有所體會。究其原因還是在於對 call 函式的原理的研究。call 函式執行的時候到底幹了什麼????直接貼上程式碼(摘自CSDN: 深入JS系列(一:call, apply, bind實現) ):
Function.prototype.es3Call = function (context) { var content = context || window; content.fn = this; var args = []; // arguments是類陣列物件,遍歷之前需要儲存長度,過濾出第一個傳參 for (var i = 1, len = arguments.length ; i < len; i++) { // 避免object之類傳入 args.push('arguments[' + i + ']'); } var result = eval('content.fn('+args+')'); delete content.fn; return result; }
在本機上除錯後發現,執行 fn1.call.call(fn2); 的結果與 fn1.es3Call.es3Call(fn2);的結果一致。說明其基本還原了call函式的原理。故結合原理程式碼總結就是:
1:把傳入的第一個引數作為 call 函式內部的一個臨時物件 context;
2:給 context 物件一個屬性 fn , 我稱呼其為實際執行函式 context.fn ;讓 this 關鍵字(僅僅是關鍵字,而不是this物件) 指向這個屬性 ,即 context.fn = this ; 注意 : 在這裡的 this 物件指向的是呼叫call()函式的函式物件。 如 fn1.call(fn2);在執行 call 函式時,call 函式內部的this指向的是fn1;然而 fn1.call.call(fn2);在執行 call() 函式時( 注意這裡必須是打了小括號“()”才算執行函式,fn1.call訪問的是一個物件 ),call函式內部的 this 指向的是 fn1.call 。
3:將傳入call函式的其他引數,放入臨時陣列arr[];
4:利用 eval (筆者採用es3的方法實現,也可以利用其他方式實現)。執行 context.fn( [args] ) ; 實際就是執行 this( [args] ); 結合第2點。
5:執行完成後再把 context.fn 刪除。返回執行 this( [args] ) 的結果。
總結上邊 5 點之後,能夠大概解釋出 fn1.call.call(fn2);的執行結果為什麼是 輸出 2 了。
首先 呼叫call 函式時,也就是 fn1.call. call(fn2) ;加粗部分;先將 fn2 作為 臨時的 context 物件 。然後 將 fn1.call這個函式物件作為 實際執行函式屬性 : context.fn = fn1.call; 注意:fn1.call會通過原型鏈找到最終的物件。其本質為 Function.prototype.call; 然後檢查其他引數,沒有了。直接執行 fn1.call()函式 ,即 context.fn();此時函式的本質還是 Function.prototype.call 函式物件。不過執行這個函式的環境還是在 Function.prototype.call()中,只不過是第一次呼叫的call()函式中。 第一次呼叫的call()函式將this關鍵字指向了 fn2 ;故而 在 fn1.call. call(fn2) ; 加粗部分的 函式中執行的 call函式執行過程中的 this指向的是 fn2; 傳入的引數為空,故而 新的 call()函式物件 的this關鍵字 被替換為window; 而執行 this()時,就是執行 fn2();不涉及 this操作。故最終輸出2。
這樣就能夠較好的解釋 fn1.call.call(fn2);的輸出結果了。為了驗證這個過程。可以這段程式碼檢視各個最終執行函式的this物件的指向:
function func(){ console.log(this); } func.call(func);//輸出func func.call.call(func); //輸出window
至於 func 為什麼指向 window MDN官網上有具體解釋(如下圖)。如果執行 func.call.call(func,2);還會出來結果 Number{2}。

以上。就是我目前對 js 中call 函式的理解。