什么`setTimeout`绑定`this`到?

时间:2015-11-02 16:39:20

标签: javascript node.js

我正在尝试创建一个示例,说明为什么习惯用语var that = this是必要的(例如,如here所述)。

因此,我开始使用错误代码的示例,该代码无法正确绑定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解释为指向global中的Node.js对象}(message变量所在的位置)?

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

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

2 个答案:

答案 0 :(得分:5)

在Node.js中,调用setTimeout的回调,并将Timeout对象绑定为上下文(this)对象,但他们没有其中定义的message。这就是第二种方法打印undefined的原因。您可以看到相应的代码段here

  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);

答案 1 :(得分:0)

您可以通过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等),它具有全局上下文作为其默认一个。这主要是因为函数调用必须具有上下文,但在这种情况下,没有特定的上下文与该函数相关联,因此它默认为全局函数。

请注意,我们所说的功能不是原型的一部分。