setTimeout 中的 this 關(guān)鍵字

原文首發(fā)于 baishusama.github.io,歡迎圍觀~

概述

本文主要講述了幾種場景下 setTimeout 的回調(diào)函數(shù)的 this 綁定出錯的幾種解決方法。

舉個栗子

class Person {
  sayName() {
    console.log("Hello, I'm " + this.name + ".")
  }
  
  constructor(name = "No One") {
    this.name = name
    setTimeout(this.sayName, 1000)
  }
}

var imo = new Person("Imo")

setTimeout(imo.sayName, 2000)

P.S. 上述代碼使用了 ES6 語法,這里不做語法上的過多解釋。不知所謂的童鞋可以看一下《30 分鐘掌握 ES6 核心內(nèi)容(上)》《30 分鐘掌握 ES6 核心內(nèi)容(下)》這兩篇。

上述代碼定義了一個 Person 類,這個類有一個構(gòu)造函數(shù)和一個 sayName 方法。其中,sayName 方法內(nèi)部使用了 this 關(guān)鍵字,用于引用當前實例的(公有成員)變量 name

上述代碼中,有兩個 setTimeout 函數(shù)。第一個 setTimeout 存在于 Person 類的構(gòu)造函數(shù)當中,其第一個參數(shù)是 this.sayName;第二個存在于全局作用域中,其第一個參數(shù)是 imo.sayName。兩者都期望在控制臺打印 Hello, I'm Imo.,但是目前兩者都沒有達到期望——目前打印的均是 Hello, I'm .。出現(xiàn)這種現(xiàn)象的原因是 sayName 方法內(nèi)部的 this 沒有如期地指向 imo 對象,而是錯誤地指向了全局對象,因而 this.name 的值為 undefined,對應到字符串是 '' (空字符串)。

那么,我們?nèi)绾涡薷拇a使得 this 關(guān)鍵字如期地指向 imo 對象呢?

第二個 setTimeout

先解決比較簡單的第二個 setTimeout 。

有兩種方法:

  1. .bind()
  2. 匿名函數(shù)包裹

代碼如下:

// 第二個 setTimeout 用“.bind()”方法修改如下:
setTimeout(imo.sayName.bind(imo), 2000)

// 第二個 setTimeout 用“匿名函數(shù)包裹”方法修改如下:
setTimeout(function(){
    imo.sayName()
}, 2000)

.bind()”方法

Apply 調(diào)用模式 - The Apply Invocation Pattern

Function.prototype.bind() @MDN:“bind() 方法會創(chuàng)建一個新函數(shù)。當這個新函數(shù)被調(diào)用時,bind() 的第一個參數(shù)將作為它運行時的 this ...”

“匿名函數(shù)包裹”方法

方法調(diào)用模式 - The Method Invocation Pattern

在外層匿名函數(shù)的內(nèi)部,sayName 作為 imo 對象的方法被調(diào)用,方法內(nèi)部的 this 也就被綁定到 imo 對象了。

第一個 setTimeout

我們嘗試上面提到的兩種解決方法。

.bind()”方法

.bind() 方法依舊可行,代碼:

// 第一個 setTimeout 用“.bind()”方法修改如下:
setTimeout(this.sayName.bind(this), 1000)

“匿名函數(shù)包裹”方法

“匿名函數(shù)包裹”方法好像讓事情更復雜了——修改后但沒能解決問題的代碼如下:

// 第二個 setTimeout 用“匿名函數(shù)包裹”方法修改如下:
setTimeout(function(){
    this.sayName()
}, 1000)

此時,this.sayName 的值為 undefined,sayName 方法甚至都沒有機會得到調(diào)用。

“匿名函數(shù)包裹+.bind

在包裹匿名函數(shù)后的代碼的基礎(chǔ)上,仍可以使用 .bind 方法。

// 第二個 setTimeout 用“匿名函數(shù)包裹+.bind”方法修改如下:
setTimeout(function(){
    this.sayName()
}.bind(this), 1000)

“匿名函數(shù)包裹+that

可以利用匿名函數(shù)形成閉包,引用正確的 this

// 第二個 setTimeout 用“匿名函數(shù)包裹+that”方法修改如下:
var that = this
setTimeout(function(){
    that.sayName()
}, 1000)

“箭頭函數(shù)”方法

ES6 的箭頭函數(shù)沒有自己的 this 、直接繼承外部作用域的 this ,所以還可以這么改:

// 第二個 setTimeout 用“箭頭函數(shù)”方法修改如下:
setTimeout(()=>{this.sayName()}, 1000)

小結(jié)

setTimeout 的回調(diào)函數(shù)中出現(xiàn) this 的時候,要特別注意其綁定的對象是否和預想的一致。當綁定有誤時可以通過下述方法解決。

setTimeout 的回調(diào)函數(shù)是一個能夠通過變量引用的對象的方法(類似于例子中的 imo.sayName)時,有兩種解決方法:

  1. .bind()”方法
  2. “匿名函數(shù)包裹”方法

setTimeout 的回調(diào)函數(shù)是一個通過 this 訪問的方法(類似于例子中的 this.sayName)時,有兩種解決方法:

  1. .bind()”方法
  2. “ES6箭頭函數(shù)”方法

setTimeout 的回調(diào)函數(shù)是一個含有 this 的匿名函數(shù)(類似于例子中的 this.sayName 被匿名函數(shù)包裹后)時,有兩種解決方法:

  1. .bind()”方法
  2. “閉包that”方法

可以看出,解決方法中的“.bind()”方法是萬金油。所以,如果你記不住這么多方法,至少也要記住“.bind()”方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、ES6簡介 ? 歷時將近6年的時間來制定的新 ECMAScript 標準 ECMAScript 6(亦稱 ...
    一歲一枯榮_閱讀 6,208評論 8 25
  • 1. this之謎 在JavaScript中,this是當前執(zhí)行函數(shù)的上下文。因為JavaScript有4種不同的...
    百里少龍閱讀 1,093評論 0 3
  • 函數(shù)參數(shù)的默認值 基本用法 在ES6之前,不能直接為函數(shù)的參數(shù)指定默認值,只能采用變通的方法。 上面代碼檢查函數(shù)l...
    呼呼哥閱讀 3,703評論 0 1
  • 箭頭函數(shù)與傳統(tǒng)JavaScript的不同 先來談談ES5中的this 在ES5中,每個函數(shù)在被調(diào)用時都會自動取得t...
    打鐵大師閱讀 1,385評論 4 21
  • 聽了很多的心靈雞湯也看過很多反雞湯的文章節(jié)目,所有的這一切都是希望能讓自己的生活按照自己的想法去過。但往往自己的想...
    飛旅爺閱讀 346評論 0 0

友情鏈接更多精彩內(nèi)容