用中国话学this指向
- 这篇文章针对有JS基础,但始终无法搞清楚this指向的同学
- 如果你知道call和apply以及bind方法,那就再好不过了
- 你还需要知道汉语当中的,第一人称和第二人称以及第三人称的区别
先看个新闻
“我体内的恶魔已被锁住了这么多年,现在这种锁链已经松了。”
“我很害怕,很孤独,很疑惑,我将要向导致我的痛苦的根源——社会作出反击,我想尽我所能地去伤害这个社会,然后死去。”
这段令人不寒而栗的文字,是美国北达科他州系列杀人案嫌犯约瑟夫·邓肯于2005年5月11日写下的。
当人们在近两个月后看到这段文字时,他已经将一个 5口之家的3人残忍地杀害,并绑架了另外2名分别为8岁和9岁的孩子。
假设,你是一个警察,抓到了一个嫌疑犯
然后你在他的家里搜出这本日记,此时你作何感想?
你肯定会想,如果这是嫌疑人自己写的,这不就等于认罪了嘛?
当然,犯罪嫌疑人也可以狡辩说,这日记根本不是我写的,只是我从网上摘抄的段子
其实, 我们只需要将上面那段文字稍微做一丢丢的改动, 这份有力的杀人证据,瞬间就会变得不那么有力了
他 体内的恶魔已被锁住了这么多年,现在这种锁链已经松了。 他 很害怕,很孤独,很疑惑, 他 将要向导致 他 的痛苦的根源——社会作出反击, 他 想尽 他 所能地去伤害这个社会,然后死去。
发现没有? 仅仅只是把第一人称, 改为了第三人称。
你很难因为这个而定嫌疑犯的罪。 因为这看上去太像个小说了
为什么会有这样的差别呢?
带着对这个问题的思考, 我们来开始今天this指向的学习。
1.this的英文含义
先看英文解释 : this: 这样,这个
接下来看一段代码
var obj = {
show: function() {
console.log(this)
}
}
obj.show();
结果很容易预测,打印obj
对象本身
在JS中,this属于一个关键字,也就是可以理解为,它是一个系统自带命令
通常,我们把它的含义解释为:当前对象
那么问题来了: 当前对象到底是指谁呢?
在上面的代码案例中,this代表的就是obj
这个对象
接下来我们再看一段代码
function show() {
console.log(this);
}
show();
结果打印window
对象
如果你对这个打印结果感到奇怪,那么可能你忽略了一个常识问题
window对象是可以省略不写的!
所以,上面的代码,实际上等价于:
function show() {
console.log(this);
}
window.show();
我们再来看一个常见的例子,关于事件绑定
btn.onclick = function() {
console.log(this);
}
btn.onclick(); //手动调用函数
//鼠标单击按钮,也可以由浏览器自动调用函数
最终,无论是手动调用,还是单击鼠标浏览器自动调用
打印结果都是btn
对象
2.我们似乎总结出了一个this指向的规律
this总是指向,调用函数的对象
如果你的代码结构是这样的
对象.函数();
那么,函数里的this,必然指向这个对象本身
假设这个结论是成立的, 我们不妨来验证一下我们的猜想!!
function show(){
console.log(this);
}
window.show(); //打印window对象
var obj1 = {};
obj1.show1 = show;
obj1.show1(); //打印obj1对象
var obj2 = {};
obj2.show2 = obj.show1;
obj2.show2(); //打印obj2对象
从上面代码的例子中,可以看出来
window
对象和obj1
对象和obj2
对象,共享了一个函数 show
window.show = obj1.show1; //true
window.show = obj2.show2; //true
三个对象,用了同一个函数
但打印出的this是各不相同的
window.show();
打印出window对象
obj1.show1();
打印出obj1对象
obj2.show2();
打印出obj2对象
这似乎再一次印证了,我们刚才的猜想: 函数由哪个对象调用,this就指向哪个对象
3.科学是严谨的,得出结论之前,我们还是要反复验证
再看一个例子:
btn.onclick = dunction() { setTimeout(function() { console.log(this); },0)}btn.onclick();
实际上,我只是在原来代码的基础上,增加了一个延迟器,并且时间设为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
所以你看,同样的一个字,它可以指代任何人,关键看从谁的嘴里说出来
function() { //this,就相当于中文里的我 //不要上一行来就问this会指向谁 //我们必须搞清楚,fn函数最终由谁调用的(相当于这句话从谁的嘴里说出来) //如果我们不能弄清楚这个问题,讨论this指向就没有意义 console.log(this);}
5.到目前为止,我们差不多可以得出结论了,下面用几个练习最终验证一下
var obj = { show: function(){ console.log(this); }}btn.onclick = function() { window.setTimeout(function(){ obj.show(); },100)}btn.onclick();
上面的代码,最终打印obj
对象
无论经过多少曲折,我们最终只看一个结论,那就是,this所在的函数,由哪个对象调用?
我把代码进一步改造
function fn(){ console.log(this);}var obj = { show: fn;}btn.onclick = function(){ window.setTimeout(function(){ obj.show(); },100)}
上面的代码,最终打印还是obj
对象
当然了,也总会有一些例外情况, 比如下面这个:
function m1(){ function m2(){ console.log(this); } m2();}m1();
我们不禁要问,函数m2是由哪个对象调用的?
我们想尽了各种可能,最终发现都是错的。我们始终不知道这个m2由哪个对象调用,好像它就那样执行了
而实际的打印结果呢?
不出意外,还是window
对象
最后的结论
- 所有的this关键字,在函数运行时,才能确定它的指向
- this所在的函数由哪个对象调用,this就会指向谁
- 当函数执行时,没有明确的调用对象时,则this指向window
6.由this衍生出的问题
刚才遗留了一个问题没有解决
btn.onclick = function() { setTimeout(functioni(){ console.log(this); },0)}btn.onclick();
我们期待this指向btn,而this现在却指向了window
这个问题该怎么修复呢? 有很多办法
如果你不知道call、apply、bind,那么恐怕你只能看得懂方法A
//办法Abtn.onclick = function() { var self = this; //使用变量保存this,self的值是不会随着函数改变的 setTimeout(function(){ console.log(self); },0);}
//办法Bbtn.onclick = function(){ var self = this; //使用变量保存this function fn(){ console.log(this); } setTimeout(function(){ fn.call(self); //使用call方法调用函数,强行制定this为self对象 },0)}
//办法Cbtn.onclick = function(){ var self = this; //使用变量保存this function fn(){ //将代码写在一个函数fn中 console.log(this); } setTimeout(function(){ fn.apply(self); //使用apply方法调用函数,强行指定this为slef对象 },0)}
//办法Dbtn.onclick = function(){ setTimeout(function()){ console.log(this); }.bind(this),0);//使用bind方法,将定时器函数的this强行绑定为时间函数的this}
7.接下来的内容,学完ES6的箭头函数再来吧
1. 如何判断箭头函数的this?
因为箭头函数不具备自己的this,所以非常简单,假装它不存在,就像这样:
这下this的指向非常清晰了吧
2. 箭头函数可以用call来改变this指向吗?
不能!! 试图改变箭头函数的this是徒劳的。
var fnn = ()=>{ console.log(this);}fnn.call(document); //依然打印window
8.最后一个特例构造函数
1. 什么是构造函数?
假设有一个函数Fn, 我们有两种方式来调用它
- 普通的调用
Fn()
- 配合
new
关键字来调用new Fn()
第二种调用方式, 函数就变成了构造函数
注意
,在构造函数中, 上面我们所讲的结论,是不成立的!!
2. 那构造函数里的this是谁呢?
请期待下一篇文章《构造函数与class》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。