摘要:最后重點理解結論箭頭函數的,總是指向定義時所在的對象,而不是運行時所在的對象。輸出,箭頭函數不會綁定所以傳入指向無效。原因是,要徹底理解應該是建立在已經大致理解了中的執行上下文,作用域作用域鏈,閉包,變量對象,函數執行過程的基礎上。
本文共 2025 字,看完只需 8 分鐘概述
前面的文章講解了 JavaScript 中的執行上下文,作用域,變量對象,this 的相關原理,但是我后來在網上看到一些例題的時候,依然沒能全做對,說明自己有些細節還沒能掌握,本文就結合例題進行深入實踐,討論函數在不同的調用方式 this 的指向問題。
老規矩,先給結論 1 和 結論2:
this 始終指向最后調用它的對象“箭頭函數”的this,總是指向定義時所在的對象,而不是運行時所在的對象。
特別提示:
本文的例子,最好自己在瀏覽器控制臺中去試一遍,看完過兩天就會忘的,一定要實踐。
// 例 1 var name = "window"; function foo() { var name = "inner"; console.log(this.name); } foo(); // ?
輸出:
window
例 1 中,非嚴格模式,由于 foo 函數是在全局環境中被調用,this 會被默認指向全局對象 window;
所以符合了我們的結論一:
this 始終指向最后調用它的對象二、一般函數和箭頭函數的對象調用
// 例 2 var name = "window"; var person = { name: "inner", show1: function () { console.log(this.name); }, show2: () => { console.log(this.name); } } person.show1(); // ? person.show2(); // ?
輸出:
inner
window
person.show1() 輸出 inner 沒毛病,person.show2() 箭頭函數為什么會輸出 window 呢。MDN 中對 this 的定義是:
箭頭函數不綁定 this, 箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。
再看本文前面給的結論:
“箭頭函數”的this,總是指向定義時所在的對象,而不是運行時所在的對象。
由于 JS 中只有全局作用域和函數作用域,箭頭函數在定義時的上一層作用域是全局環境,全局環境中的 this 指向全局對象本身,即 window。
三、call// 例 3 var name = "window" var person1 = { name: "person1", show1: function () { console.log(this.name) }, show2: () => console.log(this.name), show3: function () { return function () { console.log(this.name) } }, show4: function () { return () => console.log(this.name) } } var person2 = { name: "person2" } person1.show1() // ? person1.show1.call(person2) // ? person1.show2() // ? person1.show2.call(person2) // ? person1.show3()() // ? person1.show3().call(person2) // ? person1.show3.call(person2)() // ? person1.show4()() // ? person1.show4().call(person2) // ? person1.show4.call(person2)() // ?
輸出:
person1
person2window
windowwindow
person2
windowperson1
person1
person2
上面 10 行打印,你對了幾個呢?
首先:
person1.show1() 和 person1.show1.call(person2) 輸出結果應該沒問題,call 的作用就是改變了調用的對象 為 person2。
其次:
person1.show2(),person1.show2.call(person2),由于調用的是箭頭函數,和本文例 2 中是一樣的,箭頭函數定義時 this 指向的是上一層,也就是全局對象, 并且 箭頭函數不綁定自己的 this, 所以通過 call() 或 apply() 方法調用箭頭函數時,只能傳遞參數,不能傳遞新的對象進行綁定。故打印的值都是 window。
進而:
function foo () { return function () { console.log(this.name) } } foo()();
博客前面的文章有講過閉包,上面這段代碼也是典型的閉包運用,可以看作:
function foo () { return function () { console.log(this.name) } } var bar = foo(); bar();
所以,很明顯,被返回的內部函數其實是在全局環境下被調用的。回到前面看我們的結論 1,this 始終指向最后調用函數的對象,這句話的關鍵詞應該是什么?我覺得應該是 調用,什么時候調用,誰調用。
再回過頭來看:
person1.show3()() 輸出 window,因為內部函數在全局環境中被調用。
person1.show3().call(person2) 輸出 person2, 因為內部函數被 person2 對象調用了。
person1.show3.call(person2)() 輸出 window,也是因為內部函數在全局環境中被調用。
最后:
重點理解結論 2:
“箭頭函數”的this,總是指向定義時所在的對象,而不是運行時所在的對象。
show4: function () { return () => console.log(this.name) }
這段代碼中,箭頭函數是在 外層函數 show4 執行后才被定義的。為什么?可以翻看我前面關于作用域鏈,執行上下文,變量對象的文章,函數在進入執行階段時,會先查找內部的變量和函數聲明,將他們作為變量對象的屬性,關聯作用域鏈,并綁定 this 指向。
所以:
person1.show4()() 輸出 person1,因為外部函數在執行時的 this 為 person1, 此時定義了內部函數,而內部函數為外部函數的 this。
person1.show4().call(person2) 輸出 person1,箭頭函數不會綁定 this, 所以 call 傳入 this 指向無效。
person1.show4.call(person2)() 輸出 person2,因為外部函數在執行時的 this 為 person2,此時定義了內部函數,而內部函數為外部函數的 this。
四、構造函數中的 this// 例 4 var name = "window" function Person (name) { this.name = name; this.show1 = function () { console.log(this.name) } this.show2 = () => console.log(this.name) this.show3 = function () { return function () { console.log(this.name) } } this.show4 = function () { return () => console.log(this.name) } } var personA = new Person("personA") var personB = new Person("personB") personA.show1() // personA.show1.call(personB) // personA.show2() // personA.show2.call(personB) // personA.show3()() // personA.show3().call(personB) // personA.show3.call(personB)() // personA.show4()() // personA.show4().call(personB) // personA.show4.call(personB)() //
輸出:
personA
personBpersonA
personAwindow
personB
windowpersonA
personA
personB
例 4 和 例 3 大致一樣,唯一的區別在于兩點:
構造函數中 this 指向被創建的實例
構造函數,也是函數,所以存在作用域,所以里面的箭頭函數,它們的 this 指向,來自于上一層,就不再是全局環境 window, 而是構造函數 的 this。
五、setTimeout 函數// 例 5 function foo(){ setTimeout(() =>{ console.log("id:", this.id) setTimeout(() =>{ console.log("id:", this.id) }, 100); }, 100); } foo.call({id: 111}); //
輸出:
111
111
注意一點:
setTimeout 函數是在全局環境被 window 對象執行的,但是 foo 函數在執行時,setTimtout 委托的匿名箭頭函數被定義,箭頭函數的 this 來自于上層函數 foo 的調用對象, 所以打印結果才為 111;
// 例 6 function foo1(){ setTimeout(() =>{ console.log("id:", this.id) setTimeout(function (){ console.log("id:", this.id) }, 100); }, 100); } function foo2(){ setTimeout(function() { console.log("id:", this.id) setTimeout(() => { console.log("id:", this.id) }, 100); }, 100); } foo1.call({ id: 111 }); // ? foo2.call({ id: 222 }); // ?
輸出:
111
undefinedundefined
undefined
例 5 中已經提到,setTimeout函數被 window 對象調用,如果
是普通函數,內部的 this 自然指向了全局對象下的 id, 所以為 undefined,如果是箭頭函數,this 指向的就是外部函數的 this。
// 例 7 function foo() { return () => { return () => { return () => { console.log("id:", this.id); }; }; }; } var f = foo.call({id: 1}); var t1 = f.call({id: 2})()(); // var t2 = f().call({id: 3})(); // var t3 = f()().call({id: 4}); //
輸出:
1
1
1
這段代碼是為了鞏固我們的結論2:
“箭頭函數”的this,總是指向定義時所在的對象,而不是運行時所在的對象。
foo.call({}) 在執行時,內部的第一層箭頭函數才被定義
箭頭函數無法綁定 this, 所以 call 函數指定 this 無效
箭頭函數的 this 來自于上一層作用域(非箭頭函數作用域)的 this
總結有本書中有提到,當理解 JavaScript 中的 this 之后,JavaScript 才算入門,我深以為然。
原因是,要徹底理解 this, 應該是建立在已經大致理解了 JS 中的執行上下文,作用域、作用域鏈,閉包,變量對象,函數執行過程的基礎上。
有興趣深入了解上下文,作用域,閉包相關內容的同學可以翻看我之前的文章。
1:this、apply、call、bind
2: 從這兩套題,重新認識JS的this、作用域、閉包、對象
3: 關于箭頭函數this的理解幾乎完全是錯誤的
4: 深入JS系列
歡迎關注我的個人公眾號“謝南波”,專注分享原創文章。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98761.html
摘要:第二問直接調用函數,相當于調用,那么與函數無關。第三問先執行了函數,然后調用函數的返回值對象的屬性函數。第四問再次調用函數時,此時函數已經被第三問執行時修改,所以結果為。 JavaScript函數調用的經典例題 很多初學JavaScript的人對函數的調用理解有些模糊的,話不多說,直接上題: function Foo() { getName = function...
摘要:是完全的面向對象語言,它們通過類的形式組織函數和變量,使之不能脫離對象存在。而在基于原型的面向對象方式中,對象則是依靠構造器利用原型構造出來的。 JavaScript 函數式腳本語言特性以及其看似隨意的編寫風格,導致長期以來人們對這一門語言的誤解,即認為 JavaScript 不是一門面向對象的語言,或者只是部分具備一些面向對象的特征。本文將回歸面向對象本意,從對語言感悟的角度闡述為什...
溫馨提示:作者的爬坑記錄,對你等大神完全沒有價值,別在我這浪費生命 這一切,源于阮大神博文學習Javascript閉包(Closure)- 阮一峰中的一道思考題 //問題1: var name = The Window; var object = { name : My Object, getNameFunc : function(){ return function(){ ...
摘要:理解的函數基礎要搞好深入淺出原型使用原型模型,雖然這經常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統的類繼承還要強大。中文指南基本操作指南二繼續熟悉的幾對方法,包括,,。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家帶來幫助....(據說是阿里的前端妹子寫的) this 的值到底...
閱讀 2025·2023-04-25 14:50
閱讀 2907·2021-11-17 09:33
閱讀 2611·2019-08-30 13:07
閱讀 2838·2019-08-29 16:57
閱讀 908·2019-08-29 15:26
閱讀 3540·2019-08-29 13:08
閱讀 1990·2019-08-29 12:32
閱讀 3383·2019-08-26 13:57