一文搞定setTimeout中的this指向|8月更文挑战

722 阅读2分钟

当你向 setTimeout() (或者其他函数)传递一个函数时,该函数中的this指向跟你的期望可能不同。

由setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致,这些代码中包含的 this 关键字会指向 window (严格模式和非严格模式都会指向window)。

举例:

let myArray = ["zero", "one", "two"];
myArray.myMethod = function (sProperty) {
    alert(arguments.length > 0 ? this[sProperty] : this);
};

myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"

执行的输出结果与设想不同:

setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, "1"); // prints "undefined" after 1.5 seconds

myArray.myMethod函数传递给 setTimeout,到了定时时间,this没有指向,默认指向window对象。并且没有方法把 thisArg 传递给setTimeout,正如Array方法的forEachreduce等。下面的例子表示使用call方法设置this也没用。

setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object" 
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error

可能的解决方案:

包装函数来设置this:

setTimeout(function(){myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(function(){myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds

或者箭头函数也可以:

ES6中的箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj,因此利用箭头函数就可以轻松解决这个问题。

setTimeout(() => {myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(() => {myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds

最后整理一下:

1、setTimeout中的延迟执行代码中的this永远都指向window ;

2、setTimeout(this.method, time)这种形式中的this,即上文中提到的第一个this,是根据上下文来判断的,默认为全局作用域,但不一定总是处于全局下,具体问题具体分析;

3、setTimeout(匿名函数, time)这种形式下,匿名函数中的变量也需要根据上下文来判断,在这里匿名函数的使用形成了一个闭包,从而能访问到外层函数的局部变量。只是这种闭包,跟常见的闭包不同,因为函数式放在setTimeout里面。