用中国话学 this 指向(旧知识新讲)

这篇文章针对有 JS 基础,但始终无法搞清楚 this 指向的同学
如果你知道 call 和 apply 以及 bind 方法,那就再好不过了
你还需要知道汉语当中的,第一人称和第二人称以及第三人称的区别
先看个新闻
“我体内的恶魔已被锁住了这么多年,现在这种锁链已经松了。”“我很害怕,很孤独,很疑惑,我将要向导致我的痛苦的根源——社会作出反击,我想尽我所能地去伤害这个社会,然后死去。”
这段令人不寒而栗的文字,是美国北达科他州系列杀人案嫌犯约瑟夫·邓肯于 2005 年 5 月 11 日写下的。
当人们在近两个月后看到这段文字时,他已经将一个 5 口之家的 3 人残忍地杀害,并绑架了另外 2 名分别为 8 岁和 9 岁的孩子。
假设,你是一个警察,抓到了一个嫌疑犯
然后你在他的家里搜出这本日记,此时你作何感想?
你肯定会想,如果这是嫌疑人自己写的,这不就等于认罪了嘛?
当然,犯罪嫌疑人也可以狡辩说,这日记根本不是我写的,只是我从网上摘抄的段子
其实, 我们只需要将上面那段文字稍微做一丢丢的改动, 这份有力的杀人证据,瞬间就会变得不那么有力了
他 体内的恶魔已被锁住了这么多年,现在这种锁链已经松了。 他 很害怕,很孤独,很疑惑, 他 将要向导致 他 的痛苦的根源——社会作出反击, 他 想尽 他 所能地去伤害这个社会,然后死去。
发现没有? 仅仅只是把第一人称, 改为了第三人称。
你很难因为这个而定嫌疑犯的罪。 因为这看上去太像个小说了
为什么会有这样的差别呢?
带着对这个问题的思考, 我们来开始今天 this 指向的学习。
1.this 的英文含义
先看英文解释 : this: 这样,这个
接下来看一段代码
结果很容易预测,打印obj
对象本身
在 JS 中,this 属于一个关键字,也就是可以理解为,它是一个系统自带命令
通常,我们把它的含义解释为:当前对象
那么问题来了: 当前对象到底是指谁呢?
在上面的代码案例中,this 代表的就是obj
这个对象
接下来我们再看一段代码
结果打印window
对象
如果你对这个打印结果感到奇怪,那么可能你忽略了一个常识问题
window 对象是可以省略不写的!
所以,上面的代码,实际上等价于:
我们再来看一个常见的例子,关于事件绑定
最终,无论是手动调用,还是单击鼠标浏览器自动调用
打印结果都是btn
对象
2.我们似乎总结出了一个 this 指向的规律
this 总是指向,调用函数的对象
如果你的代码结构是这样的
那么,函数里的 this,必然指向这个对象本身
假设这个结论是成立的, 我们不妨来验证一下我们的猜想!!
从上面代码的例子中,可以看出来
window
对象和obj1
对象和obj2
对象,共享了一个函数 show
三个对象,用了同一个函数
但打印出的 this 是各不相同的
window.show();
打印出 window 对象
obj1.show1();
打印出 obj1 对象
obj2.show2();
打印出 obj2 对象
这似乎再一次印证了,我们刚才的猜想: 函数由哪个对象调用,this就指向哪个对象
3.科学是严谨的,得出结论之前,我们还是要反复验证
再看一个例子:

实际上,我只是在原来代码的基础上,增加了一个延迟器,并且时间设为 0
那么打印出的 this 会不会有变化呢??
你可以先思考一番
通常按照直觉,我们会认为,延迟器只是延缓了执行时间,打印结果依然还是 btn 对象,没有变化
但经过测试发现,实际的打印结果,是 window
对象
是我们刚才的猜想错了吗?
要解释这个现象,我们得重新来观察这段代码

注意代码当中出现了两个函数,我们分别起名字叫做函数 A 和函数 B
按照我们刚才的猜想: 函数由哪个对象调用,this就指向哪个对象
所以,this 指向会依赖它所在的函数
而这个函数,到底是 函数A
还是函数B
呢?
其实你不难从代码中看的出来, this
很明显是在函数B
中的
所以, 结果没有打印出 btn
, 现在我们也不感到奇怪了
因为, this
已经不再函数 A
的内部了,而是函数B
的内部
你可能还要问,为什么函数B
里的 this
指向window
呢?
这里其实算是一个特例,传入定时器的函数,由哪个对象调用,我们不得而知
这种情况,this
通通指向window
你暂时记住这个规律就好了,等你学完了作用域链,你就会明白其中的本质
4.回到我们开头的新闻
假设日记就是嫌疑人写的。 但日记里全是第三人称。那么 "他" 到底是谁就很难说了
反过来如果日记里用的都是第一人称写的。 那么 "我" 肯定指的是嫌疑人自己
JS 函数当中的this
关键字, 就相当于我们说话中的第一人称代词我
例如这样一个例子:
A 对 B 说:“我要杀了你!”
这里的我指代 A, 你 指代 B
B 对 A 说:”我要弄死你!”
这里的我指代 B, 你 指代 A
所以你看,同样的一个字,它可以指代任何人,关键看从谁的嘴里说出来

5.到目前为止,我们差不多可以得出结论了,下面用几个练习最终验证一下

上面的代码,最终打印obj
对象
无论经过多少曲折,我们最终只看一个结论,那就是,this所在的函数,由哪个对象调用?
我把代码进一步改造

上面的代码,最终打印还是obj
对象
当然了,也总会有一些例外情况, 比如下面这个:

我们不禁要问,函数 m2 是由哪个对象调用的?
我们想尽了各种可能,最终发现都是错的。我们始终不知道这个 m2 由哪个对象调用,好像它就那样执行了
而实际的打印结果呢?
不出意外,还是window
对象
最后的结论
所有的 this 关键字,在函数运行时,才能确定它的指向
this 所在的函数由哪个对象调用,this 就会指向谁
当函数执行时,没有明确的调用对象时,则 this 指向 window
6.由 this 衍生出的问题
刚才遗留了一个问题没有解决

我们期待 this 指向 btn,而 this 现在却指向了 window
这个问题该怎么修复呢? 有很多办法
如果你不知道 call、apply、bind,那么恐怕你只能看得懂方法 A


7.接下来的内容,学完 ES6 的箭头函数再来吧
1. 如何判断箭头函数的 this?
因为箭头函数不具备自己的 this,所以非常简单,假装它不存在,就像这样:

这下 this 的指向非常清晰了吧
2. 箭头函数可以用 call 来改变 this 指向吗?
不能!! 试图改变箭头函数的 this 是徒劳的。

8.最后一个特例构造函数
1. 什么是构造函数?
假设有一个函数 Fn, 我们有两种方式来调用它
普通的调用
Fn()
配合
new
关键字来调用new Fn()
第二种调用方式, 函数就变成了构造函数
注意
,在构造函数中, 上面我们所讲的结论,是不成立的!!
2. 那构造函数里的 this 是谁呢?
请期待下一篇文章《构造函数与 class》
评论