溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么理解并掌握J(rèn)avaScript中的this關(guān)鍵字

發(fā)布時(shí)間:2021-11-05 16:49:01 來源:億速云 閱讀:99 作者:iii 欄目:web開發(fā)

這篇文章主要講解了“怎么理解并掌握J(rèn)avaScript中的this關(guān)鍵字”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么理解并掌握J(rèn)avaScript中的this關(guān)鍵字”吧!

JavaScript 中的 this

JavaScript 引擎在查找 this 時(shí)不會(huì)通過原型鏈一層一層的查找,因?yàn)?this  完全是在函數(shù)調(diào)用時(shí)才可以確定的,讓我們來看下面幾種函數(shù)調(diào)用的形式。

1. Function Invocation Pattern

普通的函數(shù)調(diào)用,這是我們使用較多的一種, foo 是以單獨(dú)的變量出現(xiàn)而不是屬性。其中的 this 指向全局對(duì)象。

function foo() {   console.log(this) }  foo() // Window

函數(shù)作為對(duì)象的方法調(diào)用,會(huì)通過 obj.func 或者 obj[func] 的形式調(diào)用。其中的 this 指向調(diào)用它的對(duì)象。

const obj = {   name: 'lxfriday',   getName(){     console.log(this.name)   } }  obj.getName() // lxfriday

2. Constructor Pattern

通過 new Constructor() 的形式調(diào)用,其 this 會(huì)指向新生成的對(duì)象。

function Person(name){   this.name = name }  const person = new Person('lxfriday') console.log(person.name) // lxfriday

3. Apply Pattern

通過 foo.apply(thisObj) 或者 foo.call(thisObj) 的形式調(diào)用,其中的 this 指向 thisObj。如果  thisObj 是 null 或者 undefined ,其中的 this 會(huì)指向全局上下文 Window(在瀏覽器中)。

掌握以上的幾種函數(shù)調(diào)用形式就基本可以覆蓋開發(fā)中遇到的常見問題了,下面我翻譯了一篇文章,幫助你更深入的理解 this。

如果你已經(jīng)使用過一些 JavaScript 庫,你一定會(huì)注意到一個(gè)特殊的關(guān)鍵字 this。

this 在 JavaScript 中很常見,但是有很多開發(fā)人員花了很多時(shí)間來完全理解 this 關(guān)鍵字的確切功能以及在代碼中何處使用。

在這篇文章中,我將幫助您深入了解 this 其機(jī)制。

在深入了解之前,請(qǐng)確保已在系統(tǒng)上安裝了 Node 。然后,打開命令終端并運(yùn)行 node 命令。

全局環(huán)境中的 this

this 的工作機(jī)制并不容易理解。為了理解 this 是如何工作的,我們將探索不同環(huán)境中的 this。首先我們從全局上下文開始。

在全局層面中,this 等同于全局對(duì)象,在 Node repl(交互式命令行) 環(huán)境中叫 global。

$ node > this === global true

但上述情況只出現(xiàn)在 Node repl 環(huán)境中,如果我們?cè)?JS 文件中跑相同的代碼,我們將會(huì)得到不同的答案。

為了測試,我們創(chuàng)建一個(gè) index.js 的文件,并添加下面的代碼:

console.log(this === global);

然后通過 node 命令運(yùn)行:

$ node index.js false

出現(xiàn)上面情況的原因是在 JS 文件中, this 指向 module.exports,并不是指向 global。

函數(shù)中的 this

Function Invocation Pattern

在函數(shù)中 this 的指向取決于函數(shù)的調(diào)用形式。所以,函數(shù)每次執(zhí)行的時(shí)候,可能擁有不同的 this 指向。

在 index.js 文件中,編寫一個(gè)非常簡單的函數(shù)來檢查 this 是否指向全局對(duì)象:

function fat() {   console.log(this === global) } fat()

如果我們?cè)?Node repl 環(huán)境執(zhí)行上面的代碼,將會(huì)得到 true,但是如果添加 use strict 到首行,將會(huì)得到 false,因?yàn)檫@個(gè)時(shí)候  this 的值為 undefined。

為了進(jìn)一步說明這一點(diǎn),讓我們創(chuàng)建一個(gè)定義超級(jí)英雄的真實(shí)姓名和英雄姓名的簡單函數(shù)。

function Hero(heroName, realName) {   this.realName = realName;   this.heroName = heroName; } const superman= Hero("Superman", "Clark Kent"); console.log(superman);

請(qǐng)注意,這個(gè)函數(shù)不是在嚴(yán)格模式下執(zhí)行的。代碼在 node 中運(yùn)行將不會(huì)出現(xiàn)我們預(yù)期的 Superman 和 Clark Kent ,我們將得到  undefined。

這背后的原因是由于該函數(shù)不是以嚴(yán)格模式編寫的,所以 this 引用了全局對(duì)象。

如果我們?cè)趪?yán)格模式下運(yùn)行這段代碼,會(huì)因?yàn)?JavaScript 不允許給 undefined  增加屬性而出現(xiàn)錯(cuò)誤。這實(shí)際上是一件好事,因?yàn)樗柚刮覀儎?chuàng)建全局變量。

最后,以大寫形式編寫函數(shù)的名稱意味著我們需要使用 new 運(yùn)算符將其作為構(gòu)造函數(shù)來調(diào)用。將上面的代碼片段的最后兩行替換為:

const superman = new Hero("Superman", "Clark Kent"); console.log(superman);

再次運(yùn)行 node index.js 命令,您現(xiàn)在將獲得預(yù)期的輸出。

構(gòu)造函數(shù)中的 this

Constructor Pattern

JavaScript 沒有任何特殊的構(gòu)造函數(shù)。我們所能做的就是使用 new 運(yùn)算符將函數(shù)調(diào)用轉(zhuǎn)換為構(gòu)造函數(shù)調(diào)用,如上一節(jié)所示。

進(jìn)行構(gòu)造函數(shù)調(diào)用時(shí),將創(chuàng)建一個(gè)新對(duì)象并將其設(shè)置為函數(shù)的 this 參數(shù)。然后,從函數(shù)隱式返回該對(duì)象,除非我們有另一個(gè)要顯式返回的對(duì)象。

在 hero 函數(shù)內(nèi)部編寫以下 return 語句:

return {   heroName: "Batman",   realName: "Bruce Wayne", };

如果現(xiàn)在運(yùn)行 node 命令,我們將看到 return 語句將覆蓋構(gòu)造函數(shù)調(diào)用。

當(dāng) return 語句嘗試返回不是對(duì)象的任何東西時(shí),將隱式返回 this。

方法中的 this

Method Invocation Pattern

當(dāng)將函數(shù)作為對(duì)象的方法調(diào)用時(shí),this 指向該對(duì)象,然后將該對(duì)象稱為該函數(shù)調(diào)用的接收者。

在下面代碼中,有一個(gè) dialogue 方法在 hero 對(duì)象內(nèi)。通過 hero.dialogue() 形式調(diào)用時(shí),dialogue 中的 this  就會(huì)指向 hero 本身。這里,hero 就是 dialogue 方法調(diào)用的接收者。

const hero = {   heroName: "Batman",   dialogue() {     console.log(`I am ${this.heroName}!`);   } }; hero.dialogue();

上面的代碼非常簡單,但是實(shí)際開發(fā)時(shí)有可能方法調(diào)用的接收者并不是原對(duì)象??聪旅娴拇a:

const saying = hero.dialogue(); saying();

這里,我們把方法賦值給一個(gè)變量,然后執(zhí)行這個(gè)變量指向的函數(shù),你會(huì)發(fā)現(xiàn) this 的值是 undefined。這是因?yàn)?dialogue  方法已經(jīng)無法跟蹤原來的接收者對(duì)象,函數(shù)現(xiàn)在指向的是全局對(duì)象。

當(dāng)我們將一個(gè)方法作為回調(diào)傳遞給另一個(gè)方法時(shí),通常會(huì)發(fā)生接收器的丟失。我們可以通過添加包裝函數(shù)或使用 bind 方法將 this  綁定到特定對(duì)象來解決此問題。

call、apply

Apply Pattern

盡管函數(shù)的 this 值是隱式設(shè)置的,但我們也可以通過 call()和 apply() 顯式地綁定 this。

讓我們像這樣重組前面的代碼片段:

function dialogue () {   console.log (`I am ${this.heroName}`); } const hero = {   heroName: 'Batman', };

我們需要將hero 對(duì)象作為接收器與 dialogue 函數(shù)連接。為此,我們可以使用 call() 或 apply() 來實(shí)現(xiàn)連接:

dialogue.call(hero) // or dialogue.apply(hero)

需要注意的是,在非嚴(yán)格模式下,如果傳遞 null 或者 undefined 給 call 、 apply 作為上下文,將會(huì)導(dǎo)致 this  指向全局對(duì)象。

function dialogue() {   console.log('this', this) } const hero = {   heroName: 'Batman', } console.log(dialogue.call(null))

上述代碼,在嚴(yán)格模式下輸出 null,非嚴(yán)格模式下輸出全局對(duì)象。

bind

當(dāng)我們將一個(gè)方法作為回調(diào)傳遞給另一個(gè)函數(shù)時(shí),始終存在丟失該方法的預(yù)期接收者的風(fēng)險(xiǎn),導(dǎo)致將 this 參數(shù)設(shè)置為全局對(duì)象。

bind() 方法允許我們將 this 參數(shù)永久綁定到函數(shù)。因此,在下面的代碼片段中,bind 將創(chuàng)建一個(gè)新 dialogue 函數(shù)并將其 this  值設(shè)置為 hero。

const hero = {   heroName: "Batman",   dialogue() {     console.log(`I am ${this.heroName}`);   } }; // 1s 后打?。篒 am Batman setTimeout(hero.dialogue.bind(hero), 1000);

注意:對(duì)于用 bind 綁定 this 之后新生成的函數(shù),使用 call 或者 apply 方法無法更改這個(gè)新函數(shù)的 this。

箭頭函數(shù)中的 this

箭頭函數(shù)和普通函數(shù)有很大的不同,引用阮一峰 ES6入門第六章中的介紹:

  • 函數(shù)體內(nèi)的 this 對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象;

  • 不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用 new 命令,否則會(huì)拋出一個(gè)錯(cuò)誤;

  • 不可以使用 arguments 對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。如果要用,可以用 rest 參數(shù)代替;

  • 不可以使用 yield 命令,因此箭頭函數(shù)不能用作 Generator 函數(shù);

上面四點(diǎn)中,第一點(diǎn)尤其值得注意。this 對(duì)象的指向是可變的,但是在箭頭函數(shù)中,它是固定的,它只指向箭頭函數(shù)定義時(shí)的外層 this,箭頭函數(shù)沒有自己的  this,所有綁定 this 的操作,如 call apply bind 等,對(duì)箭頭函數(shù)中的 this 綁定都是無效的。

讓們看下面的代碼:

const batman = this; const bruce = () => {   console.log(this === batman); }; bruce();

在這里,我們將 this 的值存儲(chǔ)在變量中,然后將該值與箭頭函數(shù)內(nèi)部的 this 值進(jìn)行比較。node index.js 執(zhí)行時(shí)將會(huì)輸出  true。

那箭頭函數(shù)中的 this 可以做哪些事情呢?

箭頭函數(shù)可以幫助我們?cè)诨卣{(diào)中訪問 this??匆幌挛以谙旅鎸懙?counter 對(duì)象:

const counter = {   count: 0,   increase() {     setInterval(function() {       console.log(++this.count);     }, 1000);   } } counter.increase();

運(yùn)行上面的代碼,會(huì)打印 NaN。這是因?yàn)?this.count 沒有指向 counter 對(duì)象。它實(shí)際上指向全局對(duì)象。

要使此計(jì)數(shù)器工作,可以用箭頭函數(shù)重寫,下面代碼將會(huì)正常運(yùn)行:

const counter = {   count: 0,   increase () {     setInterval (() => {       console.log (++this.count);     }, 1000);   }, }; counter.increase ();

類中的 this

類是所有 JavaScript 應(yīng)用程序中最重要的部分之一。讓我們看看類內(nèi)部 this 的行為。

一個(gè)類通常包含一個(gè) constructor,其中 this 將指向新創(chuàng)建的對(duì)象。

但是,在使用方法的情況下,如果該方法以普通函數(shù)的形式調(diào)用,則 this 也可以指向任何其他值。就像一個(gè)方法一樣,類也可能無法跟蹤接收者。

我們用類重寫上面的 Hero 函數(shù)。此類將包含構(gòu)造函數(shù)和 dialogue() 方法。最后,我們創(chuàng)建此類的實(shí)例并調(diào)用該 dialogue 方法。

class Hero {   constructor(heroName) {     this.heroName = heroName;   }   dialogue() {     console.log(`I am ${this.heroName}`)   } } const batman = new Hero("Batman"); batman.dialogue();

constructor 中的 this 指向新創(chuàng)建的類實(shí)例。batman.dialogue() 調(diào)用時(shí),我們將 dialogue() 作為 batman  接收器的方法調(diào)用。

但是,如果我們存儲(chǔ)對(duì) dialogue() 方法的引用,然后將其作為函數(shù)調(diào)用,則我們將再次失去方法的接收者,而 this 現(xiàn)在指向  undefined。

為什么是指向 undefined 呢?這是因?yàn)?JavaScript 類內(nèi)部隱式以嚴(yán)格模式運(yùn)行。我們將 say()  作為一個(gè)函數(shù)調(diào)用而沒有進(jìn)行綁定。所以我們要手動(dòng)的綁定。

const say = batman.dialogue.bind(batman); say();

當(dāng)然,我們也可以在構(gòu)造函數(shù)內(nèi)部綁定:

class Hero {   constructor(heroName) {     this.heroName = heroName     thisthis.dialogue = this.dialogue.bind(this)   }   dialogue() {     console.log(`I am ${this.heroName}`)   } }

加餐:手寫 call、apply、bind

call 和 apply 的模擬實(shí)現(xiàn)大同小異,注意 apply 的參數(shù)是一個(gè)數(shù)組,綁定 this 都采用的是對(duì)象調(diào)用方法的形式。

Function.prototype.call = function(thisObj) {   thisObjthisObj = thisObj || window   const funcName = Symbol('func')   const that = this // func   thisObj[funcName] = that   const result = thisObj[funcName](...arguments)   delete thisObj[funcName]   return result }  Function.prototype.apply = function(thisObj) {   thisObjthisObj = thisObj || window   const funcName = Symbol('func')   const that = this // func   const args = arguments[1] || []   thisObj[funcName] = that   const result = thisObj[funcName](...[thisObj, ...args])   delete thisObj[funcName]   return result }  Function.prototype.bind = function(thisObj) {   thisObjthisObj = thisObj || window   const that = this // func   const outerArgs = [...arguments].slice(1)   return function(...innerArgs) {     return that.apply(thisObj, outerArgs.concat(innerArgs))   } }

感謝各位的閱讀,以上就是“怎么理解并掌握J(rèn)avaScript中的this關(guān)鍵字”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)怎么理解并掌握J(rèn)avaScript中的this關(guān)鍵字這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI