时间久了,对前端this的指向中某些细节有些遗忘,单独拿出来整理一下,主要有以下几个部分
- 默认绑定,隐式绑定,显式绑定
- 构造函数
- 箭头函数(情况多,且相对复杂)
- map()等api中回调函数的this
- 为什么某些情况使用变量重新定义this
1.默认绑定、隐式绑定、显式绑定
首先先来说说各种绑定方式之前的区别
默认绑定:
默认绑定也就是普通函数中this的绑定方式,this的指向是window
function test(){
console.log(this);
}
test() //window
隐式绑定:
最典型的就是对象中的某个属性是方法,这种this的指向就一句话:谁调用,this就指向谁!
var obj = {
name:'Tom',
color:['red','green','blue','yellow'],
test:function(){
console.log(this);
}
}
obj.test() //this指向obj(也就是当前的对象)
//多说一句,如上例,obj调用了这个test方法,在执行过程中,词法环境(执行上下文)确定了this的指向
显式绑定:
三种方式call(),apply()和bind(),这几种方式是强行改变this的指向。将改变后的指向当做第一个函数的参数传入,举个栗子
var obj = {
name:'Tom',
color:['red','green','blue','yellow'],
test:function(height,weight){
console.log(this);
console.log(height);
console.log(weight);
}
}
var db = {
name:'Jerry',
}
//this全部指向db
obj.test.call(db,'180cm','60kg');
obj.test.apply(db,['180cm','60kg']);
obj.test.bind(db,'180cm','60kg')();
顺便区分一下这三个方法
call():所有参数逐个列出来
apply():所有参数放到一个数组中
bind():参数形式和call()相同,相较于前两者,返回的是一个函数,需要再次执行,所以会是bind()()
2.构造函数
执行构造函数会创建一个新的对象,this指向这个新的对象
function Test(){
this.name='Tom';
console.log(this);
}
var res=new Test();
console.log(res.name) //Tom
3.箭头函数(情况多,且相对复杂)
大家都知道箭头函数没有自己的this,要向外部找,但是箭头函数的使用太广泛了,到处都有他的影子。回调,对象,普通函数,情况一多就容易发生混淆。一点一点梳理
1.首先箭头函数的this定义:箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。也就是说,箭头函数在定义的时候this就已经确定了。
2.箭头函数指向外部的this,如果外部是在全局环境中,那箭头函数指向window。
3.正是因为箭头函数没有自己的this,所以也不可以当做构造函数,也不能使用call(),apply()和bind()。
4.如果箭头函数被非箭头函数包含,则this绑定指向的是最近一层非箭头函数的this;否则,this的值会被设置为全局对象
全局环境下回调函数:
/*全局环境中,这里是setTimeout()和map()的回调函数,这一类型的统统指向window,像forEach(),every()*/
//箭头函数的this指向window
setTimeout(()=>{
console.log(this)
},1000)
//箭头函数的this指向window
var arr=[1,2,3];
var arrCopy=arr.map((item)=>{
console.log(this);
return item;
})
对象中的某个属性为箭头函数:
var obj={
name:'Tom',
func:()=>{
console.log(this)
}
}
obj.func();
全局环境下普通函数返回箭头函数:
function test(){
var name='Tom';
console.log(this);
return ()=>{
console.log(this);
}
}
test()(); //两次的输出都为window
/*
理解:
第一个输出为window好理解,就是普通函数this指向window
第二个输出为window是因为箭头函数被普通函数包含,this绑定指向的是最近一层非箭头函数的this
在这个例子最近一层非箭头函数就是test(),而test()的this指向就是window,所以该箭头函数的this也是
window
*/
对象中的某个属性为函数,该函数返回箭头函数:
var obj={
name:'Tom',
func:function(){
console.log(this);
return ()=>{
console.log(this);
}
}
}
obj.func()(); //两次的this都指向obj
/*
理解:
第一个输出为obj是因为obj调用了func(),谁调用,this指向谁
第二个输出为obj和上面的例子一样,尽管整体是个对象,但箭头函数依然被普通函数包含,this绑定指向的是最近一层非箭头函数的this
这里最近一层非箭头函数是func(),由于obj的调用,func()的this指向obj,所以该箭头函数的this也是obj
*/
4.map()等api中回调函数的this
回调函数中的this默认是指向window的,因为本质上是在函数内callback,并没有.前的对象调用
全局环境下
//输出window
var arr=[1,2,3];
arr.forEach(function(item){
console.log(this);
})
对象中
var obj={
num:[1,2,3,4,5],
func:function(){
this.num.forEach(function(item){
console.log(this);
})
}
}
obj.func() //输出window
5.为什么某些情况使用变量重新定义this
根本的原因在于有些情况想要的this拿不到。举个例子
var username = "Tom";
var obj = {
username:"Jerry",
getUserName(){
setTimeout(function(){
// 这里的this指向window所以正常来说会的到 Tom
console.log(this.username);
})
}
}
obj.getUserName(); // Tom
这里的getUserName()方法执行后输出的username为Tom,因为方法在回调函数中,this指向window是没有问题的,但是如果我的需求就是让setTimeout执行后输出的是Jerry,要怎么做呢?
1.显性绑定,bind()、apply()、call()随便用一个
2.箭头函数
箭头函数指向最近一层非箭头函数的this,就是这里的getUserName,obj调用的getUserName方法,this指向obj,所以最终的this.username为Jerry
var username = "Tom";
var obj = {
username:"Jerry",
getUserName(){
setTimeout(()=>{
console.log(this.username); // 这里的this指向obj
})
}
}
obj.getUserName(); // Jerry
3.变量重新定义this
使用一个变量newThis把getUserName的this存起来,再给回调函数使用。简单好用
var username = "Tom";
var obj = {
username:"Jerry",
getUserName(){
var newThis=this;
setTimeout(function(){
console.log(newThis.username); //这里的newThis指向obj
})
}
}
obj.getUserName(); // Jerry