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