理解JavaScript Call()函數原理。

  最近在作面試題的過程當中偶然碰到關於call函數的問題。而後再百度上查了查。偶然看到一篇文章: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函數的原理。故結合原理代碼總結就是:app

    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 。this

    3:將傳入call函數的其餘參數,放入臨時數組arr[];spa

    4:利用 eval (筆者採用es3的方法實現,也能夠利用其餘方式實現)。執行 context.fn( [args] ) ; 實際就是執行 this( [args] );結合第2點。.net

    5:執行完成後再把 context.fn 刪除。返回執行 this( [args] ) 的結果。prototype

   總結上邊 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。code

  這樣就可以較好的解釋 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 函數的理解。

相關文章
相關標籤/搜索