简体   繁体   中英

what does `setTimeout` bind `this` to?

I am trying to create an example that demonstrates why the idiom var that = this is necessary (eg as described here ).

So, I started with an example of erroneous code that would fail to properly bind this . However, the code I wrote gave me some unexpected results (in another direction):

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

When running under nodejs I get:

$ nodejs foo.js 
message in global scope
undefined

My question is why doesn't the second call (that uses setTimeout ) also fail in the same way and interpret this to point to the global object in Node.js (where the message variable resides)?

update When I inserted a console.log(this) inside the anonymous function, on the first invocation I get the global context object (where message resides), whereas on the second invocation (via the setTimeout ) I get the following object:

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

In Node.js, the setTimeout 's callbacks are called with a Timeout object bound as the context ( this ) object and they don't have the message defined in them. That is why the second method prints undefined . You can see the corresponding code segment 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);

You can propagate the scope to the returned function by means of the bind method:

message = "message in global scope";

var createLoggingFunctionWrongWay = function () {
    return (function () {
        console.log(this.message);
    }).bind(this);
};

setTimeout(createLoggingFunctionWrongWay.call({ message: "message" }), 1000);

Otherwise it works within its scope, that is actually leading to the global one by ignoring the one injected to the outermost function.

A simple function call, with no injected scope (that is, you are not using neither call , nor apply or bind and so on), has the global context as its default one. That's mainly because a function call must have a context, but in this case there is no particular context tied to that function, so it defaults to the global one.

Be aware that we are speaking about function that are not part of a prototype.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM