JavaScript中的this常常让人捉摸不定,用着用着你可能不知道它跟谁跑了,举个例子:
var parent={
name:"parent",
getName:function(){
return this.name;
}
}
console.log(parent.getName());//"parent"
var getName=parent.getName;
console.log(getName());//undefined
总的来说,JavaScript中的this总是指向一个对象,至于指向哪个对象需要根据具体执行环境来定。
规则1:作为对象的方法调用(隐式绑定)
var name="me";
var parent={
name:"parent",
getName:function(){
console.log(this.name);
}
}
parent.getName();//"parent"
getName是parent对象的方法,此时this会隐式的绑定给它的宿主对象parent。
规则2:作为普通函数调用(默认绑定)
var name="me";
function whoIsParent(){
console.log(this.name);
}
whoIsParent();//"me"
当函数作为普通函数而不是某个对象(非window)的属性时候,this总是指向全局对象window,或者你可以理解该普通方法是window对象的方法;
还是规则1中的例子,我们稍微改造下:
var name="me";
var parent={
name:"parent",
getName:function(){
console.log(this.name);
}
}
var getName=parent.getName;//注意
getName();
在规则1中代码的基础上加了一行代码,parent.getName被赋值给了变量getName,再调用getName方法时候,this不再指向它parent对象,而是指向了window。
注意,当一个对象的方法不再作为对象的属性调用时,该方法会变成一个普通函数,this的指向会发生变化,指向全局变量window。
再看一个非常常见的例子:
<body>
<button id="button">点我</button>
</body>
var id="window";
var $button=document.getElementById('button');
$button.onclick=function(){
var callback=function(){
console.log(this.id)
};
callback();//"window"
}
常用的解决办法是用一个变量保存上级this的引用
var id="window";
var $button=document.getElementById('button');
$button.onclick=function(){
var self=this;//保存引用
var callback=function(){
console.log(self.id)
};
callback();//"button"
}
注意,在严格模式下,普通函数内的this是不能默认指向window的
function fun(){
"use strict";
console.log(this);//undefined
}
fun();
规则3:作为构造器调用(new)
function Parent(){
this.name="parent";
}
var child=new Parent();
console.log(child.name);//"parent"
当使用new创建一个实例时,this的指向就已经确定了,它总是指向该实例。
注意:当使用new创建实例时,需注意一个问题,如果构造函数显示的返回了一个object类型的对象,那么new()的结果是返回该对象,而不是this。
function Parent(){
this.name="parent";
return{
age:30
}
}
var child=new Parent();
console.log(child.name);//undefined
如果构造器函数没有显示的返回一个object对象或者不返回任何数据,就不会造成上述问题。
function Parent(){
this.name="parent";
return "new parent";
}
var child=new Parent();
console.log(child.name);//"parent"
规则4:call,apply,bind显式指向
var parent={
name:"parent",
getName:function(){
console.log(this.name);
}
}
var obj={
name:'child'
}
var getName=parent.getName.bind(obj);
getName();
这个比较好理解,显式的修改this指向,改哪指哪,parent.getName.bind(obj)显示的将getName方法指向obj调用。
规则5:箭头函数在定义时this就已经确定
function Parent(){
this.name="parent";
this.getName=function(){
console.log(this.name);
}
}
var child=new Parent();
var getName=child.getName;
getName();//undefined,作为普通函数调用,所以this指向window
稍加修改:
function Parent(){
this.name="parent";
this.getName=()=>{
console.log(this.name);
}
}
var child=new Parent();
child.name="child";
var getName=child.getName;
getName();//"child"
修改后this.getName是一个箭头函数,定义的时候this指向child,所以this.name等于"child"。
再看个例子:
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo();//21
foo作为普通函数调用,所以this指向window,setTimeout中箭头函数定义时this指向window,所以输出21;
再看个例子:
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 2000);//s1:2
setTimeout(() => console.log('s2: ', timer.s2), 2000);//s2:0
上面两个setInterval,一个用了箭头函数,一个用了普通函数,箭头函数定义时this指向timer,所以this.s1++相当于timer.s1++,结果为2,而普通函数的this指向window,timer.s2始终为0;
总结一下:
JavaScript中的this到底指向谁,得根据具体情况而定:
1、作为对象方法调用,this指向该对象;
2、作为普通函数调用,this指向全局对象window;
3、作为new实例调用,如果构造函数没有显示返回object类型数据,则指向该实例,反之指向被返回的object;
4、call,apply,bind能显式修改this指向;
5、箭头函数中this指向定义时所在的对象,而不是调用时所在的对象;
如果你的代码中this指偏了,考虑一下是上面哪种情况吧~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。