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)鍵字钞支,用于引用當(dāng)前實例的(公有成員)變量 name茫蛹。

上述代碼中,有兩個 setTimeout 函數(shù)烁挟。第一個 setTimeout 存在于 Person 類的構(gòu)造函數(shù)當(dāng)中婴洼,其第一個參數(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,對應(yīng)到字符串是 '' (空字符串)肩刃。

那么祟霍,我們?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ù)沸呐。當(dāng)這個新函數(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ù)包裹”方法好像讓事情更復(fù)雜了——修改后但沒能解決問題的代碼如下:

// 第二個 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é)

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

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

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

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

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

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒋畜,一起剝皮案震驚了整個濱河市声畏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌姻成,老刑警劉巖插龄,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愿棋,死亡現(xiàn)場離奇詭異,居然都是意外死亡均牢,警方通過查閱死者的電腦和手機糠雨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來徘跪,“玉大人见秤,你說我怎么就攤上這事≌娲唬” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵乎澄,是天一觀的道長突硝。 經(jīng)常有香客問我,道長置济,這世上最難降的妖魔是什么解恰? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮浙于,結(jié)果婚禮上护盈,老公的妹妹穿的比我還像新娘。我一直安慰自己羞酗,他們只是感情好腐宋,可當(dāng)我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著檀轨,像睡著了一般胸竞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上参萄,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天卫枝,我揣著相機與錄音,去河邊找鬼讹挎。 笑死校赤,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的筒溃。 我是一名探鬼主播马篮,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铡羡!你這毒婦竟也來了积蔚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤烦周,失蹤者是張志新(化名)和其女友劉穎尽爆,沒想到半個月后怎顾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡漱贱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年槐雾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幅狮。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡募强,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出崇摄,到底是詐尸還是另有隱情擎值,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布逐抑,位于F島的核電站鸠儿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏厕氨。R本人自食惡果不足惜进每,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望命斧。 院中可真熱鬧田晚,春花似錦、人聲如沸国葬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汇四。三九已至泞莉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間船殉,已是汗流浹背鲫趁。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留利虫,地道東北人挨厚。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像糠惫,于是被迫代替她去往敵國和親疫剃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容

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