问题:

setInterval和setTimeout中传入函数时,函数中的this会指向window对象


我们先来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var num = 0;
function Obj (){
this.num = 1,
this.getNum = function(){
console.log(this.num);
},
this.getNumLater = function(){
setTimeout(function(){
console.log(this.num);
}, 1000)
}
}
var obj = new Obj;
obj.getNum();//1  这里打印的是obj.num,值为1
obj.getNumLater()//0  这里打印的是window.num,值为0

从上述例子中可以看到setTimeout中函数内的this是指向了window对象这是由于setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致这些代码中包含的 this 关键字会指向 window (或全局)对象。具体可参考MDN setTimeout


解决方案

若想要让setTimeout中的this指向正确的值,可以使用以下三种比较常用的方法来使this指向正确的值:


(1) 将当前对象的this存为一个变量,定时器内的函数利用闭包来访问这个变量,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var num = 0;
function Obj (){
var that = this; //将this存为一个变量,赋值给that,此时的this指向obj
this.num = 1,
this.getNum = function(){
console.log(this.num);
},
this.getNumLater = function(){
setTimeout(function(){
console.log(that.num); //利用闭包访问that,that是一个指向obj的指针
}, 1000)
}
}
var obj = new Obj;
obj.getNum();//1  打印的是obj.num,值为1
obj.getNumLater()//1  打印的是obj.num,值为1

这种方法是将当前对象的引用放在一个变量里,定时器内部的函数来访问到这个变量,自然就可以得到当前的对象。


(2)利用bind()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var num = 0;
function Obj (){
this.num = 1,
this.getNum = function(){
console.log(this.num);
},
this.getNumLater = function(){
setTimeout(function(){
console.log(this.num);
}.bind(this), 1000) //利用bind()将this绑定到这个函数上
}
}
var obj = new Obj;
obj.getNum();//1  打印的为obj.num,值为1
obj.getNumLater()//1  打印的为obj.num,值为1

bind()方法是在Function.prototype上的一个方法,当被绑定函数执行时,bind方法会创建一个新函数,并将第一个参数作为新函数运行时的this。在这个例子中,在调用setTimeout中的函数时,bind方法创建了一个新的函数,并将this传进新的函数,执行的结果也就是正确的了。关于bind方法可参考 MDN bind


(3) 箭头函数

使用es6的箭头函数,最为常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var num = 0;
function Obj (){
this.num = 1,
this.getNum = function(){
console.log(this.num);
},
this.getNumLater = function(){
setTimeout(() => {
console.log(this.num);
}, 1000) //箭头函数中的this总是指向外层调用者,也就是Obj,
//
}
}
var obj = new Obj;
obj.getNum();//1  打印的是obj.num,值为1
obj.getNumLater()//1  打印的是obj.num,值为1

ES6中的箭头函数完全修复了this的指向,向外层定义域中,一层一层的查找 this ,直到有 this 的定义。因此利用箭头函数就可以轻松解决这个问题。