ES4&&ES6中this指向问题
问题
学习ES6箭头函数的时候,了解到箭头函数中的this与ES4中的this很不一样。ES4中this指向调用方法的对象,而ES6箭头函数中,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
听上去并不难,但是实际应用过程中,发现自己对箭头函数中的this的判断简直一塌糊涂。不过在不断的使用、测试过程中,总算发现了一点规律,理解了什么叫做定义时所在的对象,而不是使用时所在的对象
。
为了便于理解,将理论与实际结合,我准备了几段代码,涉及到常见的几种情况,分析如何判断this的指向
在写这篇blog的时候,在不断的实验与思考中,我感觉自己总结出了在任何情况下都能判断this指向的规律,这是一种比较靠近原理层面的解释逻辑,经过测试,逻辑自洽且圆润。赶紧把灵感记录下来,以作备忘
演示
正式开始前有几点要牢牢记住,这算是判断this指向的总纲:
- 就近原则,this的判断过程就是一个沿着作用域链向上查找的过程,找到了最近的
this
指向就不要再向上查找了 - 箭头函数中实际上是没有this的,所以箭头函数中的this与上层最近的非箭头函数作用域中的this指向相同
- 箭头函数也就这么一点特殊:
箭头函数本身没有this
- 箭头函数也就这么一点特殊:
- 所有的this指向,指向的其实并不是当前对象/函数,而是当前作用域的执行环境
- 构造函数特殊在new操作符,会将构造函数的指向对象赋给新生的这个实例
- 如果没有new操作符,构造函数只是一个普通的函数,定义在全局中,执行环境也是全局,this指向window
Tip:找{}
大括号,当前this所属作用域是被哪个大括号包裹,一般就是this的指向,箭头函数要向上找一级大括号。
代码一:验证总纲第三点
总纲三:所有的this指向,指向的其实并不是当前对象/函数,而是当前作用域的执行环境
对象:
let user = {
test:this,
show:function(){
console.log(this);
}
};
console.log(user.test); // Window
user.show(); // user
show的执行环境是user对象,user的执行环境是Window对象,这就很清楚明白了。
构造函数:
function User(name,age){
this.age = age;
this.name = name;
this.test = this;
this.show = function(){
console.log(this);
}
}
let user = new User;
console.log(user.test); // user
user.show(); // user
解析:
show的执行环境是User,没毛病。
但是User构造函数的执行环境本来应该是Window,只是创建对象的时候,new关键字将构造函数中this赋给了新建对象user
function test(){
console.log(this);
}
test(); // Window
普通函数中this指向它的执行环境Window,this。如果构造函数不通过new创建对象,其中的this也应该指向Window
代码二:对象中的箭头函数
let obj = {
a:10,
test:this,
show:()=>{
}
};
obj.show(); // Window
console.log(obj.test); // Window
[外链图片转存失败(img-F7DSlPz6-1566699180946)(media/15663869460267/15664351048642.jpg)]
show为箭头函数,箭头函数本身没有this,强行使用,它的this与最近的作用域的this指向相同,即它的this与obj的this相同。obj对象的执行环境是Window,this指向Window。
代码三:回调函数
因为经常用到,这里以计时器setTimeout
为例。
setTimeout(function(){
console.log(this); // Window
},500);
let obj = {
name:"Linya",
age:18,
show:function(){
setTimeout(function(){
console.log(this);
},500);
}
};
obj.show(); // Window
其实回调函数中的this
是默认指向Window
对象的,因为其本质上就是在函数内callback
,并没有. 符号
前的对象调用。
下面代码就是模拟setTimeout的执行结构
function User(){
this.test = function(fn){
console.log(1);
console.log(this); // User
fn();
}
}
let user = new User();
user.test(function(){
console.log(this); // Window
})
test
方法与setTimeout
类似,都是一个对象的方法,且参数都有回调函数。其中的回调函数中的this都指向了Window
。这说明回调函数中的this
并不是随着作用域链向上移动,而是直接指向了最根本的Window
对象
一个方法的参数如果是回调函数,那么他就无法很好的调用方法所处对象中的属性。
解决办法:使用箭头函数
let obj = {
name:"Linya",
age:18,
show:function(){
setTimeout(()=>{
console.log(this);
},500);
}
};
obj.show(); // Obj
箭头函数中没有this
,强行使用,this
指向与上一级作用域this
指向相同,即与show
方法的this
指向相同。show
的执行环境为obj
,this指向obj