如何理解fn1.call.call(fn2)的結果

問題復現

有這麼一道題:面試

function fn1(){ 
    console.log(`fn1 ${this}`)
} 
function fn2(){ 
    console.log(`fn2 ${this}`)
} 
fn1.call(fn2);// fn1 function fn2(){console.log(`fn2 ${this}`)}
fn1.call.call(fn2);// fn2 [object Window]
fn1.call.call.call.call(fn2);// fn2 [object Window]

問題分析

關於call咱們通常知道這些:app

  • 用途
    主要做用是改變函數的this指向,能夠用於實現繼承
  • 傳參:
function.call(thisArg, arg1, arg2, ...)
  • 執行步驟
  1. 改邊函數的this指向
  2. 傳參並執行函數

因此對於函數

fn1.call(fn2); // fn1 function fn2(){console.log(`fn2 ${this}`)}

這樣的輸出我並不意外,由於這段代碼的意思我理解爲改變將fn1this指向fn2並執行。本質上執行的仍是執行fn1
而對於this

fn1.call.call(fn2);// fn2 [object Window]

這個樣的輸出就比較困惑了,那麼這種該怎理解呢?.net

問題解決

要理解這個輸出咱們須要知道這兩點:prototype

key_1: call方法是哪裏來的?

首先callFunction基類原型上的方法,也就是Function.prototype.call
因此fn1.call.call(fn2)至關於Function.prototype.call.call(fn2)code

key_2: call的執行過程是什麼樣的

用僞代碼表示,call的執行過程大概是這樣(不考慮傳參的狀況)的blog

Function.prototype.call = function (context) {
    // 1. 首先明確第一個this,由於這是個原型上的方法,是公共的方法,因此咱們須要知道是誰在調用call這個方法,誰在調用這個this就是誰,我給這裏的this起個別名就是this_caller
    // 2. 改變this_caller中的this指向context
    // 3. 執行this_caller()
    this();
}

知道這兩點以後,結合fn1.call.call(fn2)實例分析就是:繼承

  1. 根據key_1,咱們知道因此,fn1.call.call(fn2)的第一個callFunction.prototype.call,第二個call是經過Function.prototype.call的原型鏈找到的。
  2. 根據上面獲得的信息,咱們把能夠把fn1.call.call(fn2)看作(fn1.call).call(fn2)。結合key_2咱們能夠知道,第二個call起了這些做用:ip

    1. 首選明確這個callthis_callerfn1.call
    2. fn1.call裏面的this指向變成了fn2
    3. fn1.call的變成了fn2

3.總體來看,就是fn1.call.call(fn2)變成了fn2()

上面的問題關鍵在於:
fn1裏面雖然沒有this,不受call改變this指向的影響,可是fn1.call裏面有this,受call改變this指向的影響。
若是可以理解這一點的話,那這個問題就比較好理解了。

參考文檔

相關文章
相關標籤/搜索