Zong
this & setTimeout

首先关于上一篇博客《Vue实战经验分享》最后提到的问题进行解答,exampleData的结果是:'example 1'

OK,那是什么样的业务场景会让我遇到这样的问题呢,当时我想将一段代码延迟执行,但是一开始我直接使用this,导致这句话并没有延迟执行。

那么之后我看到了这么一段很有趣的话:

inside function called by setTimeout this is NOT VueJs object (is setTimeout global object), but self, also called after 2 seconds, is still VueJs Object.

也就是说,在setTimeout方法中的this指向的是一个全局对象,而非Vue对象。简而言之就是此this非彼this

好,那么我们来认真认识一下他们吧!


关于this

在绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,在每次函数被调用时this的值也可能会不同。

this在全局上下文

在全局运行上下文中(在任何函数体外部),this指代全局对象,无论是否在严格模式下。

比如在浏览器中指window,Node中指global

以下例子运行在浏览器中:

console.log(this.document === document); // true

// 在浏览器中,全局对象为 window 对象:
console.log(this === window); // true

this.a = 37;
console.log(window.a); // 37

this在函数上下文

在函数内部,this的值取决于函数是如何调用的。

在非严格模式下执行,此时的this的值会默认设置为全局对象。然而,在严格模式下,this将保持他进入执行环境时的值,所以下面的this将会默认为undefined,如果this未被执行的上下文环境定义,那么它将会默认为undefined

function f1(){
  return this;
}

f1() === window; // true

function f2(){
  "use strict"; // 这里是严格模式
  return this;
}

f2() === undefined; // true

关于setTimeout

这里只提到关于this的问题

当你向 setTimeout() (或者其他函数也行)传递一个函数时,该函数中的this会指向一个错误的值。这个问题在 JavaScript reference 中进行了详细解释。

解释

setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致,这些代码中包含的 this 关键字会指向 window (或全局)对象,这和所期望的this的值是不一样的。查看下面的例子:

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
// let's try to pass the 'this' object
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对象传递给回调函数。

一个可用的解决 “this” 问题的方法是使用两个非原生的setTimeout()setInterval() 全局函数代替原生的。该非原生的函数通过使用Function.prototype.call 方法激活了正确的作用域。下面的代码显示了应该如何替换:

// Enable the passage of the 'this' object through the JavaScript timers
 
var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
 
window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
  return __nativeST__(vCallback instanceof Function ? function () {
    vCallback.apply(oThis, aArgs);
  } : vCallback, nDelay);
};
 
window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
  return __nativeSI__(vCallback instanceof Function ? function () {
    vCallback.apply(oThis, aArgs);
  } : vCallback, nDelay);
};

注:JavaScript 1.8.5 引入了 Function.prototype.bind() 方法,该方法允许显式地指定函数调用时 this 所指向的值 。该方法可以帮助你解决 this 指向不确定的问题。

上面这段代码中最重点的一句话就是var oThis = this

其实说了这么多,也很容易把这个问题延伸出去,那就可以聊一聊:JavaScript上下文与作用域

我只能说目前我学习到的内容还是很浅的,还做不到很好的总结。

待我再揣摩揣摩吧~

参考与学习