“setTimeout”将“this”绑定到什么

what does `setTimeout` bind `this` to?

本文关键字:什么 绑定 setTimeout this      更新时间:2023-09-26

我正试图创建一个示例来演示为什么成语var that = this是必要的(例如,如本文所述)。

因此,我从一个错误代码的例子开始,该错误代码将无法正确绑定this。然而,我写的代码(在另一个方向上)给了我一些意想不到的结果:

message = "message in global scope";
// the below erroneous code aims to demonstrate why we need
// the "var that = this" idiom to capture the "this" lexical scope
var createLoggingFunctionWrongWay = function () {
  return function () {
    console.log(this.message);
  };
};
// the below *does* print "message in global scope"
// i.e. fails in the way I expected
createLoggingFunctionWrongWay.call({message:"message"})();

// I was expecting the below to also print "message in global scope" as
// well, yet it prints "undefined"
setTimeout(createLoggingFunctionWrongWay.call({
  message: "message"
}), 1000);

当在nodejs下运行时,我得到:

$ nodejs foo.js 
message in global scope
undefined

我的问题是,为什么第二个调用(使用setTimeout)不会以同样的方式失败,并将this解释为指向Node.js中的global对象(message变量所在的位置)?

更新当我在匿名函数中插入console.log(this)时,在第一次调用中,我得到全局上下文对象(message所在的位置),而在第二次调用中(通过setTimeout),我得到以下对象:

{ _idleTimeout: 1000,
  _idlePrev: null,
  _idleNext: null,
  _idleStart: 1446483586705,
  _onTimeout: [Function],
  _repeat: false
}

在Node.js中,setTimeout的回调是用绑定为上下文(this)对象的Timeout对象调用的,并且它们没有定义message。这就是为什么第二种方法打印CCD_ 16。您可以在此处看到相应的代码段。

  var timer = new Timeout(after);
  var length = arguments.length;
  var ontimeout = callback;
  switch (length) {
    // fast cases
    case 0:
    case 1:
    case 2:
      break;
    case 3:
      ontimeout = callback.bind(timer, arguments[2]);
      break;
    case 4:
      ontimeout = callback.bind(timer, arguments[2], arguments[3]);
      break;
    case 5:
      ontimeout =
          callback.bind(timer, arguments[2], arguments[3], arguments[4]);
      break;
    // slow case
    default:
      var args = new Array(length - 2);
      for (var i = 2; i < length; i++)
        args[i - 2] = arguments[i];
      ontimeout = callback.apply.bind(callback, timer, args);

您可以通过bind方法将作用域传播到返回的函数:

message = "message in global scope";
var createLoggingFunctionWrongWay = function () {
    return (function () {
        console.log(this.message);
    }).bind(this);
};
setTimeout(createLoggingFunctionWrongWay.call({ message: "message" }), 1000);

否则,它在其范围内工作,这实际上是通过忽略注入到最外层函数的函数而导致全局函数。

一个没有注入作用域的简单函数调用(也就是说,既不使用call,也不使用applybind等等)将全局上下文作为其默认上下文。这主要是因为函数调用必须有上下文,但在这种情况下,没有特定的上下文绑定到该函数,因此它默认为全局上下文。

请注意,我们谈论的是不属于原型的函数。