最近在做面試題的過程中偶然碰到關(guān)于call函數(shù)的問題,。然后再百度上查了查,。偶然看到一篇文章:JavaScript中的call、apply,、bind深入理解 拋開其對call函數(shù)基本概念的介紹還有其他原理的介紹,。其中一段函數(shù)吸引了我。 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函數(shù)的執(zhí)行,。因為fn1中不包含對this的操作,。不過 fn1.call.call(fn2);實在是令我費解,。我一時半會沒有領(lǐng)會筆者的表達方式,。花了很長時間去領(lǐng)會,。最終還是看其他大神的博客才得以有所體會,。究其原因還是在于對 call 函數(shù)的原理的研究。call 函數(shù)執(zhí)行的時候到底干了什么,?,??,?直接粘貼代碼(摘自CSDN:深入JS系列(一:call, apply, bind實現(xiàn))): Function.prototype.es3Call = function (context) { var content = context || window; content.fn = this; var args = []; // arguments是類數(shù)組對象,,遍歷之前需要保存長度,過濾出第一個傳參 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; } 在本機上調(diào)試后發(fā)現(xiàn),,執(zhí)行 fn1.call.call(fn2); 的結(jié)果與 fn1.es3Call.es3Call(fn2),;的結(jié)果一致,。說明其基本還原了call函數(shù)的原理。故結(jié)合原理代碼總結(jié)就是: 1:把傳入的第一個參數(shù)作為 call 函數(shù)內(nèi)部的一個臨時對象 context,; 2:給 context 對象一個屬性 fn ,, 我稱呼其為實際執(zhí)行函數(shù) context.fn ;讓 this 關(guān)鍵字(僅僅是關(guān)鍵字,,而不是this對象)指向這個屬性 ,,即 context.fn = this ; 注意 : 在這里的 this 對象指向的是調(diào)用call()函數(shù)的函數(shù)對象,。如 fn1.call(fn2),;在執(zhí)行 call 函數(shù)時,call 函數(shù)內(nèi)部的this指向的是fn1,;然而 fn1.call.call(fn2),;在執(zhí)行 call() 函數(shù)時(注意這里必須是打了小括號“()”才算執(zhí)行函數(shù),fn1.call訪問的是一個對象),call函數(shù)內(nèi)部的 this 指向的是 fn1.call ,。 3:將傳入call函數(shù)的其他參數(shù),,放入臨時數(shù)組arr[]; 4:利用 eval (筆者采用es3的方法實現(xiàn),,也可以利用其他方式實現(xiàn)),。執(zhí)行 context.fn( [args] ) ; 實際就是執(zhí)行 this( [args] ),;結(jié)合第2點,。 5:執(zhí)行完成后再把 context.fn 刪除。返回執(zhí)行 this( [args] ) 的結(jié)果,。 總結(jié)上邊 5 點之后,,能夠大概解釋出 fn1.call.call(fn2);的執(zhí)行結(jié)果為什么是 輸出 2 了,。 首先 調(diào)用call 函數(shù)時,,也就是 fn1.call.call(fn2) ;加粗部分,;先將 fn2 作為 臨時的 context 對象 ,。然后 將 fn1.call這個函數(shù)對象作為 實際執(zhí)行函數(shù)屬性 : context.fn = fn1.call;注意:fn1.call會通過原型鏈找到最終的對象,。其本質(zhì)為 Function.prototype.call,; 然后檢查其他參數(shù),沒有了,。直接執(zhí)行 fn1.call()函數(shù) ,即 context.fn(),;此時函數(shù)的本質(zhì)還是 Function.prototype.call 函數(shù)對象。不過執(zhí)行這個函數(shù)的環(huán)境還是在 Function.prototype.call()中,,只不過是第一次調(diào)用的call()函數(shù)中,。第一次調(diào)用的call()函數(shù)將this關(guān)鍵字指向了 fn2 ,;故而 在 fn1.call.call(fn2) ;加粗部分的 函數(shù)中執(zhí)行的 call函數(shù)執(zhí)行過程中的 this指向的是 fn2,;傳入的參數(shù)為空,,故而 新的 call()函數(shù)對象 的this關(guān)鍵字 被替換為window; 而執(zhí)行 this()時,,就是執(zhí)行 fn2(),;不涉及 this操作。故最終輸出2,。 這樣就能夠較好的解釋 fn1.call.call(fn2),;的輸出結(jié)果了。為了驗證這個過程,??梢赃@段代碼查看各個最終執(zhí)行函數(shù)的this對象的指向: function func(){ console.log(this); } func.call(func); //輸出funcfunc.call.call(func); //輸出window 至于 func 為什么指向 window MDN官網(wǎng)上有具體解釋(如下圖),。如果執(zhí)行 func.call.call(func,2),;還會出來結(jié)果 Number{2}。 以上,。就是我目前對 js 中call 函數(shù)的理解,。 標(biāo)簽: JavaScript Call |
|