4

I am trying to create an example that demonstrates why the idiom var that = this is necessary (e.g. 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
}
Community
  • 1
  • 1
Marcus Junius Brutus
  • 23,022
  • 30
  • 155
  • 282
  • the first argulent of `call` is the context \ scope and the next arguments are the arguments of the called function [Mozilla MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fcall) – Anonymous0day Nov 02 '15 at 16:41
  • Just try `console.log(this)` – Bergi Nov 02 '15 at 16:53
  • @Bergi just updated the question with the results of `console.log(this)` – Marcus Junius Brutus Nov 02 '15 at 17:04

2 Answers2

5

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);
thefourtheye
  • 206,604
  • 43
  • 412
  • 459
0

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.

skypjack
  • 45,296
  • 16
  • 80
  • 161