有這麼一道題:面試
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, ...)
this
指向因此對於函數
fn1.call(fn2); // fn1 function fn2(){console.log(`fn2 ${this}`)}
這樣的輸出我並不意外,由於這段代碼的意思我理解爲改變將fn1
的this
指向fn2
並執行。本質上執行的仍是執行fn1
。
而對於this
fn1.call.call(fn2);// fn2 [object Window]
這個樣的輸出就比較困惑了,那麼這種該怎理解呢?.net
要理解這個輸出咱們須要知道這兩點:prototype
call
方法是哪裏來的?首先call
是Function
基類原型上的方法,也就是Function.prototype.call
。
因此fn1.call.call(fn2)
至關於Function.prototype.call.call(fn2)
code
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)
實例分析就是:繼承
key_1
,咱們知道因此,fn1.call.call(fn2)
的第一個call
是Function.prototype.call
,第二個call
是經過Function.prototype.call
的原型鏈找到的。根據上面獲得的信息,咱們把能夠把fn1.call.call(fn2)
看作(fn1.call).call(fn2)
。結合key_2
咱們能夠知道,第二個call
起了這些做用:ip
call
的this_caller
是fn1.call
fn1.call
裏面的this
指向變成了fn2
fn1.call
的變成了fn2
3.總體來看,就是fn1.call.call(fn2)
變成了fn2()
上面的問題關鍵在於:fn1
裏面雖然沒有this
,不受call
改變this指向的影響,可是fn1.call
裏面有this
,受call
改變this
指向的影響。
若是可以理解這一點的話,那這個問題就比較好理解了。