JavaScript關(guān)鍵字this的使用方法詳解
在絕大多數(shù)情況下,函數(shù)的調(diào)用方式?jīng)Q定了
this的值(運行時綁定)。this不能在執(zhí)行期間被賦值,并且在每次函數(shù)被調(diào)用時this的值也可能會不同。ES5 引入了bind方法來設(shè)置函數(shù)的this值,而不用考慮函數(shù)如何被調(diào)用的。ES2015 引入了箭頭函數(shù),箭頭函數(shù)不提供自身的this綁定(this的值將保持為閉合詞法上下文的值)。
涵義
當前執(zhí)行上下文(global、function 或 eval)的一個屬性,在非嚴格模式下,總是指向一個對象,在嚴格模式下可以是任意值。
this 可以用在構(gòu)造函數(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。
關(guān)于 this 的指向改變的理解可以參考之前編譯執(zhí)行的相關(guān)文章,這里不做贅述。稍稍重構(gòu)這個例子,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 運行時所在的對象(本例是頂層對象)。
實質(zhì)
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 屬性。原始的對象以字典結(jié)構(gòu)保存,每一個屬性名都對應一個屬性描述對象。
{
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è)計目的就是在函數(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)境時沒有設(shè)置 this 的值,this 會保持為 undefined
function f () {
"use strict"
return this
}
console.log(f() === undefined) // true箭頭函數(shù)
在箭頭函數(shù)中,this 與封閉詞法環(huán)境的 this 保持一致。在全局代碼中,它將被設(shè)置為全局對象。
const f = () => this console.log(f() === this) // true
構(gòu)造函數(shù)
當一個函數(shù)用作構(gòu)造函數(shù)時(使用new關(guān)鍵字),它的 this 被綁定到正在構(gòu)造的新對象。
function C() {
this.a = 37
}
const o = new C()
console.log(o.a) // 37DOM 事件處理函數(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ù)中類似,因為類本質(zhì)上也是函數(shù),但也有一些區(qū)別和注意事項。在類的構(gòu)造函數(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' ]不像基類的構(gòu)造函數(shù),派生類的構(gòu)造函數(shù)沒有初始的 this 綁定。派生類不能在調(diào)用 super() 之前返回,除非其構(gòu)造函數(shù)返回的是一個對象,或者根本沒有構(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對象的方法
當函數(shù)作為對象里的方法被調(diào)用時,this 被設(shè)置為調(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()) // 37this 的綁定只受最接近的成員引用的影響,如果 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()) // 5getter 與 setter
再次,相同的概念也適用于當函數(shù)在一個 getter 或者 setter 中被調(diào)用。用作 getter 或 setter 的函數(shù)都會把 this 綁定到設(shè)置或獲取屬性的對象。
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 a2forEach 方法會調(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關(guān)鍵字this的使用方法詳解的詳細內(nèi)容,更多關(guān)于JavaScript關(guān)鍵字this的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
簡單的兩種Extjs formpanel加載數(shù)據(jù)的方式
這篇文章介紹了兩種Extjs formpanel加載數(shù)據(jù)的方式,有需要的朋友可以參考一下2013-11-11
javascript 獲取鏈接文件地址中第一個斜線內(nèi)的正則表達式
我想得到“windows”,請問用正則表達式怎么寫?2009-06-06
使用Promise封裝小程序wx.request的實現(xiàn)方法
這篇文章主要介紹了使用Promise封裝小程序wx.request的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-11-11
JavaScript 中如何實現(xiàn)并發(fā)控制
在日常開發(fā)過程中,你可能會遇到并發(fā)控制的場景,比如控制請求并發(fā)數(shù)。那么在 JavaScript 中如何實現(xiàn)并發(fā)控制呢?在回答這個問題之前,我們來簡單介紹一下并發(fā)控制。2021-05-05
layui實現(xiàn)把數(shù)據(jù)表格時間戳轉(zhuǎn)換為時間格式的例子
今天小編就為大家分享一篇layui實現(xiàn)把數(shù)據(jù)表格時間戳轉(zhuǎn)換為時間格式的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09
超級簡單實現(xiàn)JavaScript MVC 樣式框架
本文給大家分享的是一則翻譯過來的,由國外友人寫的如何簡單有效的實現(xiàn)javascript MVC樣式框架,算是一個MVC的入門教程,希望大家能夠喜歡。2015-03-03

