5

I was looking at the code for the underscore.js library (jQuery does the same thing) and just wanted some clarification on why the window object is getting passed into the self executing function.

For example:

(function() {            //Line 6
  var root = this;       //Line 12
  //Bunch of code
}).call(this);           //Very Bottom

Since this is global, why is it being passed into the function? Wouldn't the following work as well? What issues would arrise doing it this way?

(function() {
  var root = this;
  //Bunch of code
}).call();
KingKongFrog
  • 13,946
  • 21
  • 75
  • 124
  • apparently it's the same, can you post the underscore js code where that is done? – jxs Dec 13 '12 at 18:02
  • No arguments are getting passed -- `call` is being used to set `this` inside the callback. Still, I also don't see why this is necessary solely from the code you've posted. – apsillers Dec 13 '12 at 18:04
  • The function starts at Line 6, variable root is decalred on line 12 and then the call is at the very bottom. The whole file is one big function. – KingKongFrog Dec 13 '12 at 18:06
  • @KingKongFrog are you saying that `var root = this;` could have been written as `var root = window;`? – Salman A Dec 13 '12 at 18:20
  • 1
    No I'm saying var root = this works regardless of passing in "this" as argument in the call. – KingKongFrog Dec 13 '12 at 18:21
  • But `window` would work also – KingKongFrog Dec 13 '12 at 18:22
  • That's not an argument in the first snippet, it's an explicit scope resolution. Usually redundant, but better safe than sorry if you were otherwise going to rely on the relatively hacky solution of default scope for anonymous functions (which is a "feature" that I spend half my time trying to avoid, and that could theoretically change in the future). – jfmatt Dec 13 '12 at 18:29
  • @jfmatt can you explain further where this would be a hacky solution by not using "explicit scope resolution" – KingKongFrog Dec 13 '12 at 18:34
  • It depends on common-but-undefined behavior that's invalid in strict mode. See the link in apsillers' answer - in ECMA5 Strict, anonymous functions with no explicit context get undefined instead of window. – jfmatt Dec 13 '12 at 18:40

2 Answers2

5

I suspect the reason is ECMAScript 5 strict mode.

In non-strict mode, this IIFE

(function() {
   console.log(this); // window or global
})();

logs the window object (or the global object, if you're on a node.js server), because the global object is supplied as this to functions that don't run as a method of an object.

Compare that result to

"use strict";
(function() {
   console.log(this); // undefined
})();

In strict mode, the this of a bare-invoked function is undefined. Therefore, the Underscore authors use call to supply an explicit this to the anonymous function so that it is standardized across strict and non-strict mode. Simply invoking the function (as I did in my examples) or using .call() leads to an inconsistency that can be solved with .call(this).

apsillers
  • 112,806
  • 17
  • 235
  • 239
1

jQuery does the same thing, and you can find several answers to your question on SO by searching along those lines.

There are two reasons to have an always-available global variable like window in local scope, both of which are for the sake of fairly minor optimizations:

  1. Loading time. Looking up a local variable takes a tiny bit less time than looking up a global variable, because the interpreter doesn't have to look as far up the context tree. Over the length of a function the size of Underscore or jQuery, that can theoretically add up to a less-trivial amount of time on slower machines.

  2. Filesize. Javascript minification relies on the fact that variables can be named anything as long as they're consistent. myLongVariableName becomes a in the minified version. The catch, of course, is that this can only apply to variables defined inside the script; anything it's pulling from outside has to keep the same name to work, because if you shrink window down to pr the interpreter won't know what the hell you're talking about. By shadowing it with a reference to itself, Minify can do things like:

    (function(A){ B(A, {...}); A.doStuff(); //etc });})(window)
    

    and every time you would otherwise have put window (6 characters) you now have A (1 character). Again, not major, but in a large file that needs to explicitly call window on a regular basis for scope management it can be important, and you've already got an advantage by a few characters if you use it twice. And in large, popular libraries that get served by limited servers and to people who might have lousy Internet plans, every byte counts.


Edit after reading comments on question:

If you look at what function.call() actually does, your second snippet wouldn't actually work - the this being passed in isn't an argument, it's an explicit calling context for the function, and you have to provide it to .call(). The line at the beginning of the file serves the two purposes above. The reason for setting the context explicitly with call is futureproofing - right now, anonymous functions get the global context, but theoretically that could change, be discouraged in a future ECMA specification, or behave oddly on a niche browser. For something that half the Internet uses, it's best to be more explicit and avoid the issue entirely. The this/window choice at the top level I think must have something to do with worrying about non-PC devices (i.e. mobile) potentially using a different name for their top-level object.

jfmatt
  • 926
  • 5
  • 14