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)的,沒有辦法事先確定到底指向哪個(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í)沒有設(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) // 37DOM 事件處理函數(shù)
當(dāng)函數(shù)被用作事件處理函數(shù)時(shí),它的 this 指向觸發(fā)事件的元素(一些瀏覽器在使用非 addEventListener 的函數(shù)動(dòng)態(tài)地添加監(jiān)聽函數(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)聽器所在的 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ù)沒有初始的 this 綁定。派生類不能在調(diào)用 super() 之前返回,除非其構(gòu)造函數(shù)返回的是一個(gè)對(duì)象,或者根本沒有構(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()) // 37this 的綁定只受最接近的成員引用的影響,如果 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()) // 5getter 與 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è)變量,是非常常見的做法,請(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 a2forEach 方法會(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 寫法
$('#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-11
javascript 獲取鏈接文件地址中第一個(gè)斜線內(nèi)的正則表達(dá)式
我想得到“windows”,請(qǐng)問(wèn)用正則表達(dá)式怎么寫?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-11
ES6 proxy和reflect的使用方法與應(yīng)用實(shí)例分析
這篇文章主要介紹了ES6 proxy和reflect的使用方法,結(jié)合具體實(shí)例形式分析了ES6 proxy和reflect基本功能、原理、使用方法與操作注意事項(xiàng),需要的朋友可以參考下2020-02-02
echarts學(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-11
JavaScript 中如何實(shí)現(xiàn)并發(fā)控制
在日常開發(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-05
layui實(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ó)外友人寫的如何簡(jiǎn)單有效的實(shí)現(xiàn)javascript MVC樣式框架,算是一個(gè)MVC的入門教程,希望大家能夠喜歡。2015-03-03

