JavaScript關(guān)鍵字this的使用方法詳解
在絕大多數(shù)情況下,函數(shù)的調(diào)用方式?jīng)Q定了
this
的值(運(yùn)行時(shí)綁定)。this
不能在執(zhí)行期間被賦值,并且在每次函數(shù)被調(diào)用時(shí)this
的值也可能會(huì)不同。ES5 引入了bind
方法來(lái)設(shè)置函數(shù)的this
值,而不用考慮函數(shù)如何被調(diào)用的。ES2015 引入了箭頭函數(shù),箭頭函數(shù)不提供自身的this
綁定(this
的值將保持為閉合詞法上下文的值)。
涵義
當(dāng)前執(zhí)行上下文(global、function 或 eval)的一個(gè)屬性,在非嚴(yán)格模式下,總是指向一個(gè)對(duì)象,在嚴(yán)格模式下可以是任意值。
this
可以用在構(gòu)造函數(shù)之中,表示實(shí)例對(duì)象。除此之外,this
還可以用在別的場(chǎng)合。但不管是什么場(chǎng)合,this
都有一個(gè)共同點(diǎn):它總是返回一個(gè)對(duì)象。簡(jiǎn)單說(shuō),this
就是屬性或方法“當(dāng)前”所在的對(duì)象。
const p = { name: 'jay', describe() { console.log(`${this.name} is a good man`) } } p.describe() // jay is a good man
上面代碼中,this.name
表示 name
屬性所在的那個(gè)對(duì)象。由于 this.name
是在 describe
方法中調(diào)用,而 describe
方法所在的當(dāng)前對(duì)象是 p
,因此 this
指向 p
,this.name
就是 p.name
。
由于對(duì)象的屬性可以賦給另一個(gè)對(duì)象,所以屬性所在的當(dāng)前對(duì)象是可變的,即 this
的指向是可變的。
const n = { name: 'jj' } n.describe = p.describe n.describe() // jj is a good man
上面代碼中,p.describe
屬性被賦給 n
,于是 n.describe
就表示 describe
方法所在的當(dāng)前對(duì)象是 n
,所以this.name
就指向 n.name
。
關(guān)于 this
的指向改變的理解可以參考之前編譯執(zhí)行的相關(guān)文章,這里不做贅述。稍稍重構(gòu)這個(gè)例子,this
的動(dòng)態(tài)指向就能看得更清楚。
function describe () { console.log(`${this.name} is a good man`) } const p = { name: 'jay', describe } const n = { name: 'jj', describe } p.describe() // jay is a good man n.describe() // jj is a good man
只要函數(shù)被賦給另一個(gè)變量,this
的指向就會(huì)變。
const p = { name: 'jay', describe: function () { console.log(`${this.name} is a good man`) } } const name = 'jj' const f = p.describe f() // jj is a good man
上面代碼中,p.describe
被賦值給變量 f
,內(nèi)部的 this
就會(huì)指向 f
運(yùn)行時(shí)所在的對(duì)象(本例是頂層對(duì)象)。
實(shí)質(zhì)
JavaScript 語(yǔ)言之中,一切皆對(duì)象,運(yùn)行環(huán)境也是對(duì)象,所以函數(shù)都是在某個(gè)對(duì)象之中運(yùn)行,this
就是函數(shù)運(yùn)行時(shí)所在的對(duì)象(環(huán)境)。JavaScript 支持運(yùn)行環(huán)境動(dòng)態(tài)切換,也就是說(shuō),this
的指向是動(dòng)態(tài)的,沒(méi)有辦法事先確定到底指向哪個(gè)對(duì)象。
const o = { n: 1 }
JavaScript 引擎會(huì)先在內(nèi)存里面,生成一個(gè)對(duì)象 { n: 1 }
,然后把這個(gè)對(duì)象的內(nèi)存地址賦值給變量 o
。也就是說(shuō),變量 o
是一個(gè)地址引用(reference)。后面如果要讀取 o.n
,引擎先從 o
拿到內(nèi)存地址,然后再?gòu)脑摰刂纷x出原始的對(duì)象,返回它的n
屬性。原始的對(duì)象以字典結(jié)構(gòu)保存,每一個(gè)屬性名都對(duì)應(yīng)一個(gè)屬性描述對(duì)象。
{ n: { [[value]]: 1 [[writable]]: true [[enumerable]]: true [[configurable]]: true } }
屬性的值可能是一個(gè)函數(shù)。
const o = { n: function () {} }
這時(shí),引擎會(huì)將函數(shù)單獨(dú)保存在內(nèi)存中,然后再將函數(shù)的地址賦值給n
屬性的 value
屬性。
{ n: { [[value]]: 函數(shù)的地址 ... } }
由于函數(shù)是一個(gè)單獨(dú)的值,所以它可以在不同的環(huán)境(上下文)執(zhí)行。
const f = function () {} const o = { f } f() // 在全局對(duì)象執(zhí)行 obj.f() // 在 obj 對(duì)象里面執(zhí)行
由于函數(shù)可以在不同的運(yùn)行環(huán)境執(zhí)行,所以需要有一種機(jī)制,能夠在函數(shù)體內(nèi)部獲得當(dāng)前的運(yùn)行環(huán)境(context)。所以,this
就出現(xiàn)了,它的設(shè)計(jì)目的就是在函數(shù)體內(nèi)部,指代函數(shù)當(dāng)前的運(yùn)行環(huán)境。
使用場(chǎng)合
this
主要有以下幾個(gè)使用場(chǎng)合。
全局上下文
無(wú)論是否在嚴(yán)格模式下,在全局執(zhí)行環(huán)境中(在任何函數(shù)體外部)this
都指向全局對(duì)象。在瀏覽器環(huán)境下,它指的就是頂層對(duì)象 window
??梢允褂?globalThis
獲取全局對(duì)象,無(wú)論你的代碼是否在當(dāng)前上下文運(yùn)行。
console.log(this === window) // true
函數(shù)上下文
在函數(shù)內(nèi)部,this
的值取決于函數(shù)被調(diào)用的方式。
function f () { return this } // 在瀏覽器中,全局對(duì)象是 window console.log(f() === window) // true //在 Node 中 console.log(f() === globalThis) // true
然而,在嚴(yán)格模式下,如果進(jìn)入執(zhí)行環(huán)境時(shí)沒(méi)有設(shè)置 this
的值,this
會(huì)保持為 undefined
function f () { "use strict" return this } console.log(f() === undefined) // true
箭頭函數(shù)
在箭頭函數(shù)中,this
與封閉詞法環(huán)境的 this
保持一致。在全局代碼中,它將被設(shè)置為全局對(duì)象。
const f = () => this console.log(f() === this) // true
構(gòu)造函數(shù)
當(dāng)一個(gè)函數(shù)用作構(gòu)造函數(shù)時(shí)(使用new關(guān)鍵字),它的 this
被綁定到正在構(gòu)造的新對(duì)象。
function C() { this.a = 37 } const o = new C() console.log(o.a) // 37
DOM 事件處理函數(shù)
當(dāng)函數(shù)被用作事件處理函數(shù)時(shí),它的 this
指向觸發(fā)事件的元素(一些瀏覽器在使用非 addEventListener
的函數(shù)動(dòng)態(tài)地添加監(jiān)聽(tīng)函數(shù)時(shí)不遵守這個(gè)約定)。
function bluify (e) { console.log(this === e.currentTarget) // true } const element = document.getElementById('do') element.addEventListener('click', bluify, false)
內(nèi)聯(lián)事件處理函數(shù)
當(dāng)代碼被內(nèi)聯(lián) on-event
處理函數(shù)調(diào)用時(shí),它的 this
指向監(jiān)聽(tīng)器所在的 DOM 元素。
<button onclick="alert(this.tagName.toLowerCase());">Show this</button>
類上下文
this
在 class
中的表現(xiàn)與在函數(shù)中類似,因?yàn)轭惐举|(zhì)上也是函數(shù),但也有一些區(qū)別和注意事項(xiàng)。在類的構(gòu)造函數(shù)中,this
是一個(gè)常規(guī)對(duì)象。類中所有非靜態(tài)的方法都會(huì)被添加到 this
的原型中。
class P { constructor () { const proto = Object.getPrototypeOf(this) console.log(Object.getOwnPropertyNames(proto)) } first () {} second () {} static third () {} } new P() // [ 'constructor', 'first', 'second' ]
不像基類的構(gòu)造函數(shù),派生類的構(gòu)造函數(shù)沒(méi)有初始的 this
綁定。派生類不能在調(diào)用 super()
之前返回,除非其構(gòu)造函數(shù)返回的是一個(gè)對(duì)象,或者根本沒(méi)有構(gòu)造函數(shù)。
class A extends P { } class B extends P { constructor() { return { a: 5 } } } class C extends P { constructor() { super() } } class D extends P { constructor() { } } new A() // ['constructor'] new B() // new C() // ['constructor'] new D() // ReferenceError: Must call super constructor in derived class before
對(duì)象的方法
當(dāng)函數(shù)作為對(duì)象里的方法被調(diào)用時(shí),this
被設(shè)置為調(diào)用該函數(shù)的對(duì)象。
const o = { prop: 37, f: function () { return this.prop } } console.log(o.f()) // 37
請(qǐng)注意,這樣的行為完全不會(huì)受函數(shù)定義方式或位置的影響。如果對(duì)象的方法里面包含 this
,this
的指向就是方法運(yùn)行時(shí)所在的對(duì)象。該方法賦值給另一個(gè)對(duì)象,就會(huì)改變this
的指向。
const o = { prop: 37 } function independent() { return this.prop } o.f = independent console.log(o.f()) // 37
this
的綁定只受最接近的成員引用的影響,如果 this
所在的方法不在對(duì)象的第一層,這時(shí) this
只是指向當(dāng)前一層的對(duì)象,而不會(huì)繼承更上面的層。
a = { p: 1, b: { p: 2, m: function() { console.log(this, this.p) } } } a.b.m() // { p: 2, m: [Function: m] }, 2
但是,下面這幾種用法,都會(huì)改變 this
的指向。
var o ={ n: function () { console.log(this) } }; o.n() // 情況一 (o.n = o.n)() // window // => (o.n = function () { console.log(this) })() // => (function () { console.log(this) })() // 情況二 (false || o.n)() // window // => (false || function () { console.log(this) })() // 情況三 (1, o.n)() // window // => (1, function () { console.log(this) })()
原型鏈
對(duì)于在對(duì)象原型鏈上某處定義的方法,同樣的概念也適用。如果該方法存在于一個(gè)對(duì)象的原型鏈上,那么 this
指向的是調(diào)用這個(gè)方法的對(duì)象,就像該方法就在這個(gè)對(duì)象上一樣。
const o = { f: function () { return this.a + this.b } } const p = Object.create(o) p.a = 1 p.b = 4 console.log(p.f()) // 5
getter 與 setter
再次,相同的概念也適用于當(dāng)函數(shù)在一個(gè) getter
或者 setter
中被調(diào)用。用作 getter
或 setter
的函數(shù)都會(huì)把 this
綁定到設(shè)置或獲取屬性的對(duì)象。
function sum() { return this.a + this.b + this.c } const o = { a: 1, b: 2, c: 3, get average() { return (this.a + this.b + this.c) / 3 } } Object.defineProperty(o, 'sum', { get: sum, enumerable: true, configurable: true }) console.log(o.average, o.sum) // 2, 6
使用注意項(xiàng)
this
的動(dòng)態(tài)切換,為 JavaScript 創(chuàng)造了巨大的靈活性,但也使得編程變得困難和模糊。所以在使用的時(shí)候,需要特別注意以下幾點(diǎn)。
注意多層this
由于 this
的指向是不確定的,所以切勿在函數(shù)中包含多層的 this
。
var o = { f1: function () { console.log(this) // {f1: ?} var f2 = function () { console.log(this) // Window }() } } o.f1()
f2
在編譯的時(shí)候提升到全局,所以是 window
。如果要使用 o
作為 this
,可以在 f1
記錄 this
然后使用。使用一個(gè)變量固定 this
的值,然后內(nèi)層函數(shù)調(diào)用這個(gè)變量,是非常常見(jiàn)的做法,請(qǐng)務(wù)必掌握。
JavaScript 提供了嚴(yán)格模式,也可以硬性避免這種問(wèn)題。嚴(yán)格模式下,如果函數(shù)內(nèi)部的 this
指向頂層對(duì)象,就會(huì)報(bào)錯(cuò)。
const counter = { count: 0 } counter.inc = function () { 'use strict' this.count++ } const f = counter.inc f() // TypeError: Cannot read properties of undefined (reading 'count')
注意數(shù)組處理方法中的this
數(shù)組的 map
和 foreach
方法,允許提供一個(gè)函數(shù)作為參數(shù)。這個(gè)函數(shù)內(nèi)部不應(yīng)該使用 this
。
const o = { v: 'hello', p: [ 'a1', 'a2' ], f: function f() { this.p.forEach(function (item) { console.log(this, `${this.v} ${item}`) }) } } o.f() // window, undefined a1 // window, undefined a2
forEach
方法會(huì)調(diào)用數(shù)組中每一項(xiàng)的 toString
方法,所以 this
指向頂層對(duì)象 window
。解決辦法同樣可以記錄 f
的 this
,然后使用。另一種方法是將 this
當(dāng)作 foreach
方法的第二個(gè)參數(shù),固定它的運(yùn)行環(huán)境。
注意回調(diào)函數(shù)中的this
回調(diào)函數(shù)中的 this
往往會(huì)改變指向,最好避免使用。
var o = new Object() o.f = function () { console.log(this) // $('#button') } // jQuery 寫(xiě)法 $('#button').on('click', o.f)
點(diǎn)擊按鈕以后,此時(shí) this
不再指向 o
對(duì)象,而是指向按鈕的 DOM
對(duì)象,因?yàn)閒方法是在按鈕對(duì)象的環(huán)境中被調(diào)用的。為了解決這個(gè)問(wèn)題,可以采用 call
、apply
、bind
方法對(duì) this
進(jìn)行綁定,也就是使得 this
固定指向某個(gè)對(duì)象,減少不確定性。
以上就是JavaScript關(guān)鍵字this的使用方法詳解的詳細(xì)內(nèi)容,更多關(guān)于JavaScript關(guān)鍵字this的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
簡(jiǎn)單的兩種Extjs formpanel加載數(shù)據(jù)的方式
這篇文章介紹了兩種Extjs formpanel加載數(shù)據(jù)的方式,有需要的朋友可以參考一下2013-11-11javascript 獲取鏈接文件地址中第一個(gè)斜線內(nèi)的正則表達(dá)式
我想得到“windows”,請(qǐng)問(wèn)用正則表達(dá)式怎么寫(xiě)?2009-06-06使用Promise封裝小程序wx.request的實(shí)現(xiàn)方法
這篇文章主要介紹了使用Promise封裝小程序wx.request的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11ES6 proxy和reflect的使用方法與應(yīng)用實(shí)例分析
這篇文章主要介紹了ES6 proxy和reflect的使用方法,結(jié)合具體實(shí)例形式分析了ES6 proxy和reflect基本功能、原理、使用方法與操作注意事項(xiàng),需要的朋友可以參考下2020-02-02echarts學(xué)習(xí)筆記之圖表自適應(yīng)問(wèn)題詳解
最近發(fā)現(xiàn)一個(gè)問(wèn)題,echarts圖初始化后不能自適應(yīng)瀏覽器的縮放,所以下面這篇文章就來(lái)給大家介紹了關(guān)于echarts圖表自適應(yīng)問(wèn)題的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11JavaScript 中如何實(shí)現(xiàn)并發(fā)控制
在日常開(kāi)發(fā)過(guò)程中,你可能會(huì)遇到并發(fā)控制的場(chǎng)景,比如控制請(qǐng)求并發(fā)數(shù)。那么在 JavaScript 中如何實(shí)現(xiàn)并發(fā)控制呢?在回答這個(gè)問(wèn)題之前,我們來(lái)簡(jiǎn)單介紹一下并發(fā)控制。2021-05-05layui實(shí)現(xiàn)把數(shù)據(jù)表格時(shí)間戳轉(zhuǎn)換為時(shí)間格式的例子
今天小編就為大家分享一篇layui實(shí)現(xiàn)把數(shù)據(jù)表格時(shí)間戳轉(zhuǎn)換為時(shí)間格式的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09超級(jí)簡(jiǎn)單實(shí)現(xiàn)JavaScript MVC 樣式框架
本文給大家分享的是一則翻譯過(guò)來(lái)的,由國(guó)外友人寫(xiě)的如何簡(jiǎn)單有效的實(shí)現(xiàn)javascript MVC樣式框架,算是一個(gè)MVC的入門(mén)教程,希望大家能夠喜歡。2015-03-03