setTimeout 中的 this 关键字

原文首发于 baishusama.github.io,欢迎围观~

概述

本文主要讲述了几种场景下 setTimeout 的回调函数的 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 核心内容(上)》《30 分钟掌握 ES6 核心内容(下)》这两篇。

上述代码定义了一个 Person 类,这个类有一个构造函数和一个 sayName 方法。其中,sayName 方法内部使用了 this 关键字,用于引用当前实例的(公有成员)变量 name

上述代码中,有两个 setTimeout 函数。第一个 setTimeout 存在于 Person 类的构造函数当中,其第一个参数是 this.sayName;第二个存在于全局作用域中,其第一个参数是 imo.sayName。两者都期望在控制台打印 Hello, I'm Imo.,但是目前两者都没有达到期望——目前打印的均是 Hello, I'm .。出现这种现象的原因是 sayName 方法内部的 this 没有如期地指向 imo 对象,而是错误地指向了全局对象,因而 this.name 的值为 undefined,对应到字符串是 '' (空字符串)。

那么,我们如何修改代码使得 this 关键字如期地指向 imo 对象呢?

第二个 setTimeout

先解决比较简单的第二个 setTimeout

有两种方法:

  1. .bind()
  2. 匿名函数包裹

代码如下:

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

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

.bind()”方法

Apply 调用模式 - The Apply Invocation Pattern

Function.prototype.bind() @MDN:“bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this ...”

“匿名函数包裹”方法

方法调用模式 - The Method Invocation Pattern

在外层匿名函数的内部,sayName 作为 imo 对象的方法被调用,方法内部的 this 也就被绑定到 imo 对象了。

第一个 setTimeout

我们尝试上面提到的两种解决方法。

.bind()”方法

.bind() 方法依旧可行,代码:

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

“匿名函数包裹”方法

“匿名函数包裹”方法好像让事情更复杂了——修改后但没能解决问题的代码如下:

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

此时,this.sayName 的值为 undefinedsayName 方法甚至都没有机会得到调用。

“匿名函数包裹+.bind

在包裹匿名函数后的代码的基础上,仍可以使用 .bind 方法。

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

“匿名函数包裹+that

可以利用匿名函数形成闭包,引用正确的 this

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

“箭头函数”方法

ES6 的箭头函数没有自己的 this 、直接继承外部作用域的 this ,所以还可以这么改:

// 第二个 setTimeout 用“箭头函数”方法修改如下:
setTimeout(()=>{this.sayName()}, 1000)

小结

setTimeout 的回调函数中出现 this 的时候,要特别注意其绑定的对象是否和预想的一致。当绑定有误时可以通过下述方法解决。

setTimeout 的回调函数是一个能够通过变量引用的对象的方法(类似于例子中的 imo.sayName)时,有两种解决方法:

  1. .bind()”方法
  2. “匿名函数包裹”方法

setTimeout 的回调函数是一个通过 this 访问的方法(类似于例子中的 this.sayName)时,有两种解决方法:

  1. .bind()”方法
  2. “ES6箭头函数”方法

setTimeout 的回调函数是一个含有 this 的匿名函数(类似于例子中的 this.sayName 被匿名函数包裹后)时,有两种解决方法:

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 一、ES6简介 ​ 历时将近6年的时间来制定的新 ECMAScript 标准 ECMAScript 6(亦称 ...
    一岁一枯荣_阅读 5,961评论 8 25
  • 1. this之谜 在JavaScript中,this是当前执行函数的上下文。因为JavaScript有4种不同的...
    百里少龙阅读 957评论 0 3
  • 函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 上面代码检查函数l...
    呼呼哥阅读 3,269评论 0 1
  • 箭头函数与传统JavaScript的不同 先来谈谈ES5中的this 在ES5中,每个函数在被调用时都会自动取得t...
    打铁大师阅读 1,250评论 4 21
  • 听了很多的心灵鸡汤也看过很多反鸡汤的文章节目,所有的这一切都是希望能让自己的生活按照自己的想法去过。但往往自己的想...
    飞旅爷阅读 305评论 0 0