本文深入解析JavaScript中setTimeout和setInterval在对象方法调用时this指向window的问题。通过Function.prototype.bind、模拟bind的闭包以及保存this为别名(that)三种方法,提供兼容IE6-8及现代浏览器的解决方案,确保回调函数正确执行。
JavaScript中setTimeout与setInterval的this指向问题
在JavaScript中,setTimeout和setInterval函数的第一个参数可以是字符串或函数。当在对象内部使用setTimeout延时调用该对象的方法时,可能会遇到this指向的问题。
代码如下:
function obj() {
this.fn = function() {
alert("ok");
console.log(this);
setTimeout(this.fn, 1000);//直接使用this引用当前对象
}
}
var o = new obj();
o.fn(); 三种解决方法
执行上述代码会发现结果不符合预期。原因是setTimeout内部的this指向了window对象,导致试图调用window.fn,而该属性为undefined,从而引发错误。因此,问题的核心在于如何获取当前对象的正确引用,以下是三种解决方法。
代码如下:
// 方法一:
function obj() {
this.fn = function() {
alert("ok");
console.log(this);
setTimeout(this.fn.bind(this), 1000);//通过Function.prototype.bind 绑定当前对象
}
}
var o = new obj();
o.fn(); 方法一:使用bind
这种方法可以得出正确结果,但Function.prototype.bind是ES5标准新增的方法,在IE6-8中不支持,仅IE9及以上版本可用。为了实现兼容性,可以简单模拟bind方法,参考以下代码。
代码如下:
// 方法二:
function obj() {
this.fn = function() {
alert("ok");
setTimeout((function(a,b){
return function(){
b.call(a);
}
})(this,this.fn), 1000);//模拟Function.prototype.bind
}
}
var o = new obj();
o.fn(); 方法二:模拟bind
首先通过一个自执行匿名函数将当前对象和方法作为参数a和b传入,然后返回一个闭包。利用call方法使this指向正确。以下是更简洁的实现方式。
代码如下:
// 方法三:
function obj() {
this.fn = function() {
var that = this;//保存当前对象this
alert("ok");
setTimeout(function(){
that.fn();
}, 1000);//通过闭包得到当前作用域,好访问保存好的对象that
}
}
var o = new obj();
o.fn(); 方法三:保存this为别名
第三种方法的关键点在于:将当前对象的this保存为别名that,并通过闭包访问该作用域以调用保存的对象that。当对象方法内部存在多层嵌套函数或setTimeout、setInterval等导致this丢失(即指向window)的情况时,在正确的作用域中保存var that = this变得非常实用。