自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文讀懂 JavaScript 中的 this 關(guān)鍵字

開(kāi)發(fā) 前端
this 是一個(gè)令無(wú)數(shù) JavaScript 編程者又愛(ài)又恨的知識(shí)點(diǎn)。它的重要性毋庸置疑,然而真正想掌握它卻并非易事。希望本文可以幫助大家理解 this。

this 是一個(gè)令無(wú)數(shù) JavaScript 編程者又愛(ài)又恨的知識(shí)點(diǎn)。它的重要性毋庸置疑,然而真正想掌握它卻并非易事。希望本文可以幫助大家理解 this。

[[285731]]

JavaScript 中的 this

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

1. Function Invocation Pattern

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

  1. function foo() { 
  2.   console.log(this) 
  3.  
  4. foo() // Window 

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

  1. const obj = { 
  2.   name: 'lxfriday', 
  3.   getName(){ 
  4.     console.log(this.name) 
  5.   } 
  6.  
  7. obj.getName() // lxfriday 

2. Constructor Pattern

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

  1. function Person(name){ 
  2.   this.name = name 
  3.  
  4. const person = new Person('lxfriday') 
  5. console.log(person.name) // lxfriday 

3. Apply Pattern

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

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

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

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

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

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

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

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

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

  1. $ node 
  2. > this === global 
  3. true 

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

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

  1. console.log(this === global); 

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

  1. $ node index.js 
  2. false  

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

函數(shù)中的 this

Function Invocation Pattern

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

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

  1. function fat() { 
  2.   console.log(this === global) 
  3. fat() 

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

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

  1. function Hero(heroName, realName) { 
  2.   this.realName = realName; 
  3.   this.heroName = heroName; 
  4. const supermanHero("Superman", "Clark Kent"); 
  5. console.log(superman); 

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

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

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

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

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

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

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

Constructor Pattern

JavaScript 沒(méi)有任何特殊的構(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)部編寫(xiě)以下 return 語(yǔ)句:

  1. return { 
  2.   heroName: "Batman", 
  3.   realName: "Bruce Wayne", 
  4. }; 

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

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

方法中的 this

Method Invocation Pattern

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

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

  1. const hero = { 
  2.   heroName: "Batman", 
  3.   dialogue() { 
  4.     console.log(`I am ${this.heroName}!`); 
  5.   } 
  6. }; 
  7. hero.dialogue(); 

上面的代碼非常簡(jiǎn)單,但是實(shí)際開(kāi)發(fā)時(shí)有可能方法調(diào)用的接收者并不是原對(duì)象。看下面的代碼:

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

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

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

call、apply

Apply Pattern

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

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

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

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

  1. dialogue.call(hero) 
  2. // or 
  3. dialogue.apply(hero) 

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

  1. function dialogue() { 
  2.   console.log('this', this) 
  3. const hero = { 
  4.   heroName: 'Batman', 
  5. 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。

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

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

箭頭函數(shù)中的 this

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

  • 函數(shù)體內(nèi)的 this 對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象;
  • 不可以當(dāng)作構(gòu)造函數(shù),也就是說(shuō),不可以使用 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ù)沒(méi)有自己的 this,所有綁定 this 的操作,如 call apply bind 等,對(duì)箭頭函數(shù)中的 this 綁定都是無(wú)效的。

讓們看下面的代碼:

  1. const batman = this
  2. const bruce = () => { 
  3.   console.log(this === batman); 
  4. }; 
  5. bruce(); 

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

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

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

  1. const counter = { 
  2.   count: 0, 
  3.   increase() { 
  4.     setInterval(function() { 
  5.       console.log(++this.count); 
  6.     }, 1000); 
  7.   } 
  8. counter.increase(); 

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

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

  1. const counter = { 
  2.   count: 0, 
  3.   increase () { 
  4.     setInterval (() => { 
  5.       console.log (++this.count); 
  6.     }, 1000); 
  7.   }, 
  8. }; 
  9. counter.increase (); 

類(lèi)中的 this

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

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

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

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

  1. class Hero { 
  2.   constructor(heroName) { 
  3.     this.heroName = heroName; 
  4.   } 
  5.   dialogue() { 
  6.     console.log(`I am ${this.heroName}`) 
  7.   } 
  8. const batman = new Hero("Batman"); 
  9. batman.dialogue(); 

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

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

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

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

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

  1. class Hero { 
  2.   constructor(heroName) { 
  3.     this.heroName = heroName 
  4.     thisthis.dialogue = this.dialogue.bind(this) 
  5.   } 
  6.   dialogue() { 
  7.     console.log(`I am ${this.heroName}`) 
  8.   } 

加餐:手寫(xiě) call、apply、bind

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

  1. Function.prototype.call = function(thisObj) { 
  2.   thisObjthisObj = thisObj || window 
  3.   const funcName = Symbol('func') 
  4.   const that = this // func 
  5.   thisObj[funcName] = that 
  6.   const result = thisObj[funcName](...arguments) 
  7.   delete thisObj[funcName] 
  8.   return result 
  9.  
  10. Function.prototype.apply = function(thisObj) { 
  11.   thisObjthisObj = thisObj || window 
  12.   const funcName = Symbol('func') 
  13.   const that = this // func 
  14.   const args = arguments[1] || [] 
  15.   thisObj[funcName] = that 
  16.   const result = thisObj[funcName](...[thisObj, ...args]) 
  17.   delete thisObj[funcName] 
  18.   return result 
  19.  
  20. Function.prototype.bind = function(thisObj) { 
  21.   thisObjthisObj = thisObj || window 
  22.   const that = this // func 
  23.   const outerArgs = [...arguments].slice(1) 
  24.   return function(...innerArgs) { 
  25.     return that.apply(thisObj, outerArgs.concat(innerArgs)) 
  26.   } 

 

責(zé)任編輯:趙寧寧 來(lái)源: 今日頭條
相關(guān)推薦

2024-02-23 19:11:13

C++編程開(kāi)發(fā)

2024-08-09 12:44:45

JavaScript原型鏈鏈條

2017-03-07 15:13:28

Scala偏函數(shù)函數(shù)

2022-04-20 11:10:17

bias推薦系統(tǒng)debias

2023-12-22 19:59:15

2021-08-04 16:06:45

DataOps智領(lǐng)云

2020-06-23 08:41:47

JavaScript開(kāi)發(fā)技術(shù)

2018-09-28 14:06:25

前端緩存后端

2025-04-03 10:56:47

2022-09-22 09:00:46

CSS單位

2022-11-06 21:14:02

數(shù)據(jù)驅(qū)動(dòng)架構(gòu)數(shù)據(jù)

2021-09-04 19:04:14

配置LogbackJava

2009-09-28 11:34:49

Javascript

2024-03-25 08:20:41

JavaScript數(shù)組字符串

2022-07-05 06:30:54

云網(wǎng)絡(luò)網(wǎng)絡(luò)云原生

2023-05-20 17:58:31

低代碼軟件

2023-11-27 17:35:48

ComponentWeb外層

2022-10-20 08:01:23

2021-12-29 18:00:19

無(wú)損網(wǎng)絡(luò)網(wǎng)絡(luò)通信網(wǎng)絡(luò)

2022-07-26 00:00:03

語(yǔ)言模型人工智能
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)