面向对象编程中,我们经常要和this打交道。而对于函数中this到底指向哪里,对刚开始接触JavaScript的我们,经常会分不清楚。
我们必须明确的是函数中this的指向不是固定的,和函数执行时的环境有关,简单的说,我们可以理解函数中this指向的是当前调用该函数的对象,所以我们也针对以下几种情况来消化下这种说话。
1、作为函数调用时
简单的例子如下:
function a(){
console.log(this)
b();
function b(){
console.log(this)
}
}
a();
在浏览器时执行该代码时,打印出来的都是window对象。
因为作为函数调用时,默认是被认为在全局对象中调用该函数(nodejs环境中是global对象)注:不是在当前函数所处的作用域对象
这其实很难让人理解,为什么变成是全局对象,我们只能接受这种默认设定(也因为难以理解,所以现在严格模式下,默认是undefined),包括有时候我们将函数作为回调参数执行时声明的this也会是全局对象,如
setTimeout(function(){
console.log(this)
},100)
有一点需要注意的是,箭头函数(我们等等再特殊讲下)
2、作为对象方法调用时
简单的例子如下
var obj={
name:"obj",
logThis(){
console.log(this,this.name);
}
}
obj.logThis();
代码执行时,答应出来的是obj对象,name值是obj,这是满足我们的预期:函数目前就是作为obj的方法被调用,所以调用该函数的对象是obj。
这种情况我们很容易消化,但是换了一种情况就另当别论,如
var name="test";
var obj={
name:"obj",
logThis(){
console.log(this,this.name);
}
}
var fn=obj.logThis;
fn();
这个时候打印出来的window对象,name值是test
原来这个时候fn指向的是一个函数(不是指向 obj执行logThis 这个行为),fn调用就是调用一个函数,也就是第一种情况,所以上面的例子就如下:
var name="test";
var obj={
name:"obj",
logThis(){
console.log(this,this.name);
}
}
var fn=function(){
console.log(this,this.name);
}
fn();
3、作为构造函数执行
简单的例子如下
function People(){
console.log(this)
}
People.prototype={
name:"people"
};
new People();
这个时候打印出来的是我们新生成的people对象。
因为构造函数执行的过程我们可以简单理解为
1、新生成一个对象(people)
2、该对象原型对象指向构造函数的prototype对象(People.prototype)
3、通过新生成的对象调用构造函数(可以简单理解为People.call(this))
也就是在第三步的时候明确声明了当前调用函数的对象是新生成的对象
4、作为箭头函数调用
简单例子如下
var obj={
logThis(){
setTimeout(function(){
console.log(this);
})
setTimeout(()=>{
console.log(this)
})
}
}
obj.logThis();
这个时候打印的我们可能因为都是window对象,然而结果打脸了,第一个打印的是window对象,第二个打印的是obj对象
原来箭头函数,不会生成新的this指向,箭头函数在哪个环境执行,指向的就是当前环境的this对象,也就是第二种情况下,this指向的obj.logThis()执行时的this指向(前面我们提到的是obj对象)
5、apply,call,bind调用
this的指针默认指向就是上面提到的4种,但是总有需求,我们想要自定义this指向,这个时候apply,call,bind这3个方法就排的上用场
这三个方法都是函数对象自身的方法,第一个参数支持传入的是函数调用时设置的this对象,如
let obj={
name:"obj"
}
function test(){
console.log(this)
}
test();
test.call(obj);
这个时候第一个打印的是window对象(看1说明),第二个打印的是obj对象(我们通过传参的方法明确说明函数执行是的this对象是obj)
而这三个方法,不同如下
apply,call 调用时都是直接调用函数,并把第一个参数的函数作为函数的this指向
不同的是apply传递给执行函数的参数是数组格式,而call传递的参数直接附加即可
let obj={};
function test(a,b){
console.log(this,a,b)
}
test.call(obj,1,2);
test.apply(obj,[1,2]);
bind调用时是返回明确好函数this指针的新函数,而不是立即执行,需要再次调用
var test2=test.apply(obj);
test2(1,2)
上述总结希望对大家有用