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