我所理解的JavaScript中的this指向
前言
JS 中的 this
指向是一個經(jīng)常被問到的問題,網(wǎng)上也有很多文章是關(guān)于 this 的。本文整理一下我理解下的 this
以及一些我比較疑惑的關(guān)于 this
問題。
this 指向
有幾個 this
的指向問題是幾乎每篇文章都會說的,比如作為函數(shù)直接調(diào)用,作為對象的方法調(diào)用, new
運算符執(zhí)行中的 this
行為。比較通用的說法是, this
指向的是直接調(diào)用該函數(shù)的對象。其實也很好理解,就是為什么需要 this
這個關(guān)鍵字,就是我們有需要在函數(shù)內(nèi)部對調(diào)用函數(shù)的對象進行操作的需求。但是有時候我們遇到的情況并不是像書上或 mdn
上遇到的典型的情況, this
的行為可能就會讓我們感到有點疑惑。
函數(shù)的直接調(diào)用
當我們直接調(diào)用一個已經(jīng)聲明的函數(shù),那么在非嚴格模式下,該函數(shù)內(nèi)部的 this
指向的是全局對象,瀏覽器環(huán)境下就是 window
對象。
function f1(){ return this; } //在瀏覽器中: f1() === window; //在瀏覽器中,全局對象是window //在Node中: f1() === global;
當函數(shù)是在全局環(huán)境下定義的時候,這種現(xiàn)象是可以理解的,因為全局環(huán)境下定義的函數(shù)其實就是掛載在全局對象上的一個屬性,比附上面的 f1
也可以理解為 window.f1
。但我認為嚴格模式下的行為才是更符合 this
這個關(guān)鍵字的目的的,特別是我們的函數(shù)可能是在非全局環(huán)境(比如另一個函數(shù)中)定義和調(diào)用的,這種情況下 this
還指向 window
是不太合理的。所以在嚴格模式下,一個函數(shù)直接調(diào)用,它的 this
指向的是 undefined
,如果我們想要得到非嚴格模式下的結(jié)果,那我們調(diào)用函數(shù)的方法就要改為 window.f1()
,而如果函數(shù)是在非全局環(huán)境下定義的話,那么始終返回的是 undefined
。我認為這樣的行為是更符合邏輯的。
'use strict' function d () { function e() { console.log(this) } console.log(this) } d() //undefined //undefined window.d() //Window{} //undefined
這里在全局模式下使用
use strict
只是為了測試,實際使用還是盡量放在函數(shù)內(nèi)局部使用嚴格模式,全局下的嚴格模式很容易導致出錯。
函數(shù)作為對象的屬性調(diào)用
這也是在代碼中非常常見的場景,我認為這是比函數(shù)調(diào)用更好理解,也更能幫助我們理解 this 行為的場景。簡單的來說就是 this
指向的是 直接
調(diào)用函數(shù)的那個對象。并且要注意的是,這跟函數(shù)在哪里定義的是無關(guān)的,我們看 this
,看的就是從哪里調(diào)用的函數(shù)。
//在對象內(nèi)部定義 var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); // 37 //在對象外部定義 var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console.log(o.f()); // 37 //在對象內(nèi)部定義,但是給外部變量引用并執(zhí)行 var o = { prop: 37, f: function() { console.log(this) return this.prop; } }; var prop = 100; var m = o.f; console.log(m()); //Window{} //100
上面的段落我給 直接
這兩個字加粗了,想要表達的意思是當我們通過多個對象的屬性嵌套找到并調(diào)用函數(shù),那么最后那個最接近函數(shù)的對象就是函數(shù) this
的指向。
var o = { a:10, b:{ a:12, fn:function(){ console.log(this.a); //12 } } } o.b.fn(); var o = { a:10, b:{ // a:12, fn:function(){ console.log(this.a); //undefined } } } o.b.fn();
為什么我說這個場景能夠幫助我們理解,原因就是它反映出 this
這個關(guān)鍵字的本質(zhì)。JS
中的函數(shù)也是一種對象,在我們的執(zhí)行環(huán)境中的活動對象保存的也只是函數(shù)對象的一個引用,如果這個引用是保存在活動對象中的某個對象的屬性中(即我們通過活動對象中的某個對象的屬性找到該函數(shù)),那么函數(shù)執(zhí)行的時候 this
就會指向這個對象,這也是為什么多層對象的調(diào)用,還是最靠近函數(shù)的那個對象作為 this
。雖然在代碼中我們的函數(shù)是在對象中定義的,但是實際在內(nèi)存中,對象中只保存著函數(shù)的引用,函數(shù)自己是在一個單獨的內(nèi)存空間中。所以我們通過哪個對象找到函數(shù)并執(zhí)行,函數(shù)中的 this
就指向這個對象。上面的直接調(diào)用 this
返回 undefined
也是說得通的。
通過原型的調(diào)用
有時我們是通過原型來執(zhí)行公用的函數(shù),此時已然符合我們上面的邏輯,我們通過哪個實例 找到 函數(shù),那么 this 就指向那個實例。
var o = { f: function() { return this.a + this.b; } }; var p = Object.create(o); p.a = 1; p.b = 4; console.log(p.f()); // 5
箭頭函數(shù)
箭頭函數(shù)并沒有自己的 this
,箭頭函數(shù)中的 this
是它所在的執(zhí)行環(huán)境中的 this
(mdn
寫的是封閉的詞法環(huán)境),當你遇到箭頭函數(shù)中的 this
不確定的時候,你可以想象把這個箭頭函數(shù)換成 console.log(this)
,這個 console 的輸出就是箭頭函數(shù)中 this
的值,并且箭頭函數(shù)的 this 是綁定的,不會改變(有時候看上去改變了是所在的 context
改變了)。還有一點需要注意的是,用 call
,apply
,bind
來調(diào)用箭頭函數(shù),第一個參數(shù)是沒有意義的,也就是無法改變 this
,如果仍需要使用,第一個參數(shù)應該傳 null
???mdn
給出的示例。
var globalObject = this; var foo = (() => this); console.log(foo() === globalObject); // true // 接著上面的代碼 // 作為對象的一個方法調(diào)用 var obj = {foo: foo}; console.log(obj.foo() === globalObject); // true // 嘗試使用call來設(shè)定this console.log(foo.call(obj) === globalObject); // true // 嘗試使用bind來設(shè)定this foo = foo.bind(obj); console.log(foo() === globalObject); // true // 創(chuàng)建一個含有bar方法的obj對象, // bar返回一個函數(shù), // 這個函數(shù)返回this, // 這個返回的函數(shù)是以箭頭函數(shù)創(chuàng)建的, // 所以它的this被永久綁定到了它外層函數(shù)的this。 // bar的值可以在調(diào)用中設(shè)置,這反過來又設(shè)置了返回函數(shù)的值。 var obj = { bar: function() { var x = (() => this); return x; } }; // 作為obj對象的一個方法來調(diào)用bar,把它的this綁定到obj。 // 將返回的函數(shù)的引用賦值給fn。 var fn = obj.bar(); // 直接調(diào)用fn而不設(shè)置this, // 通常(即不使用箭頭函數(shù)的情況)默認為全局對象 // 若在嚴格模式則為undefined console.log(fn() === obj); // true // 但是注意,如果你只是引用obj的方法, // 而沒有調(diào)用它 var fn2 = obj.bar; // 那么調(diào)用箭頭函數(shù)后,this指向window,因為它從 bar 繼承了this。 console.log(fn2()() == window); // true
其他情況
還有一些情況我覺得比較簡單,就一筆帶過。
1. 當函數(shù)被用作事件處理函數(shù)時,它的 this
指向觸發(fā)事件的元素。
2. 當代碼被內(nèi)聯(lián) on-event
處理函數(shù)調(diào)用時,它的this指向監(jiān)聽器所在的 DOM 元素,需要注意的是只有最外層的 this
是這樣,如果里面還有嵌套函數(shù),則嵌套函數(shù)的 this
在非嚴格模式下仍然指向全局對象。
3. 構(gòu)造函數(shù)中的 this
請看之前的文章JavaScript中new操作符的解析和實現(xiàn)
4. bind
,call
和 apply
都一樣,函數(shù)的 this
被綁定到第一個參數(shù)上。
總結(jié)
以上就是我所總結(jié)的 JS
中的 this
的一些要點,如果有什么遺漏或者錯誤的地方,歡迎指正。
到此這篇關(guān)于我所理解的JavaScript中的this指向的文章就介紹到這了,更多相關(guān)JavaScript this指向內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
List the UTC Time on a Computer
List the UTC Time on a Computer...2007-06-06H5+C3+JS實現(xiàn)雙人對戰(zhàn)五子棋游戲(UI篇)
這篇文章主要為大家詳細介紹了H5+C3+JS實現(xiàn)雙人對戰(zhàn)五子棋游戲,實現(xiàn)雙人對戰(zhàn)模式,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-09-09