What advantages does using (function(window, document, undefined) { ... })(window, document) confer? [duplicate]
Asked Answered
S

4

64

I guess using this pattern is the new hotness, but I don't understand what the advantage is and I don't understand the scoping implications.

The pattern:

(function(window, document, undefined){
  window.MyObject = {
    methodA: function() { ... },
    methodB: function() { ... }
  };
})(window, document)

So I have several questions about this.

Is there a particular advantage to encapsulating an object like this?
Why are window and document being fed in instead of just being accessed normally?
Why the heck is undefined being passed in?
Is attaching the object we're creating directly to window a particularly good idea?

I'm used to what I'll call the Crockford style of Javascript encapsulation (because I got it off the Douglas Crockford Javascript videos).

NameSpace.MyObject = function() {
  // Private methods
  // These methods are available in the closure
  // but are not exposed outside the object we'll be returning.
  var methodA = function() { ... };

  // Public methods
  // We return an object that uses our private functions,
  // but only exposes the interface we want to be available.
  return {

    methodB: function() {
      var a = methodA();
    },
    methodC: function() { ... }

  }
// Note that we're executing the function here.
}();

Is one of these patterns functionally better than the other? Is the first one an evolution of the other?

Samira answered 16/2, 2011 at 18:21 Comment(2)
answer in two words: caching + sandboxing; for details, see below ;)Silkworm
See also How does this JavaScript/JQuery Syntax work: (function( window, undefined ) { })(window)?Delvalle
A
51

Why are window and document being fed in instead of just being accessed normally?

Generally to fasten the identifier resolution process, having them as local variables can help (although IMO the performance improvements may be negligible).

Passing the global object is also a widely used technique on non-browser environments, where you don't have a window identifier at the global scope, e.g.:

(function (global) {
  //..
})(this); // this on the global execution context is 
          // the global object itself

Why the heck is undefined being passed in?

This is made because the undefined global property in ECMAScript 3, is mutable, meaning that someone could change its value affecting your code, for example:

undefined = true; // mutable
(function (undefined) {
  alert(typeof undefined); // "undefined", the local identifier
})(); // <-- no value passed, undefined by default

If you look carefully undefined is actually not being passed (there's no argument on the function call), that's one of the reliable ways to get the undefined value, without using the property window.undefined.

The name undefined in JavaScript doesn't mean anything special, is not a keyword like true, false, etc..., it's just an identifier.

Just for the record, in ECMAScript 5, this property was made non-writable...

Is attaching the object we're creating directly to window a particularly good idea?

It's a common way used to declare global properties when you are on another function scope.

Arthro answered 16/2, 2011 at 18:42 Comment(2)
Why pass both window and document. Isn't document a property of window?Kowalewski
@ShaunLuttin It just makes the referencing faster. Not only that, that way you can use it the way most people are used to. You don't usually use it as: window.document.whatever(), you use it as: document.whatever(). If you don't do it and you do "document.whatever()", in reality, you are using the global window object instead of the scoped one, even though it's not clear enough. Lastely, it allows better minimization. Window can become "w" and document "d". Just by that it can reduce scripts by a large amount of characters.Dorrie
C
27

This particular style does bring some benefits over the "Crockford" style. Primarily, passing window and document allows the script to be more efficiently minified. A minifier can rename those parameters to single-character names, saving 5 and 7 bytes respectively per reference. This can add up: jQuery references window 33 times and document 91 times. Minifying each token down to one character saves 802 bytes.

Additionally, you do get an execution speed benefit. When I first read @joekarl's assertion that it provides a performance benefit, I thought, "that seems rather spurious." So I profiled the actual performance with a test page. Referencing window one hundred million times, the local variable reference provides a modest 20% speed increase (4200 ms to 3400ms) in Firefox 3.6 and a shocking 31,000% increase (13 sec to 400ms) in Chrome 9.

Of course, you're never going to reference window 100,000,000 times in practice, and even 10,000 direct references only take 1ms in Chrome, so the actual performance gain here is almost completely negligible.

Why the heck is undefined being passed in?

Because (as mentioned by @CMS) the token undefined is actually undefined. Unlike null, it has no special meaning, and you're free to assign to this identifier like any other variable name. (Note, however, that this is no longer true in ECMAScript 5.)

Is attaching the object we're creating directly to window a particularly good idea?

The window object is the global scope, so that's exactly what you're doing at some point, whether or not you explictly write "window." window.Namespace = {}; and Namespace = {}; are equivalent.

Corniculate answered 16/2, 2011 at 20:48 Comment(2)
+1 for minification advantageSackbut
Don't forget undefined -- a minifier will also rename this parameter to a short name, as well as all references in the script.Brendanbrenden
A
1

I'm with you in using Crockford's style.

To answer your questions

1.Is there a particular advantage to encapsulating an object like this?

The only advantage I can see is by making window and document local variables instead of global variables, you get some added safety by not being able to directly overwrite either one and also some performance gain by them both being local.

2.Why are window and document being fed in instead of just being accessed normally?

Addressed above. Local variables tend to be faster, but with jit compiling these days thats becoming nominal.

3.Why the heck is undefined being passed in?

No clue....

4.Is attaching the object we're creating directly to window a particularly good idea?

Probably not, but I would still stick with Crockford's pattern as attaching the function to the window object exposes it to the rest of the global stack through the window object as opposed to exposing it through a non-standard namespace.

Advection answered 16/2, 2011 at 18:33 Comment(2)
I think the reason undefined is passed in is because it IS theoretically possible to overwrite undefined (i.e., it's not really a keyword in some implementations, just a global variable initially left undefined). So you provide a third parameter, called "undefined", and then pass nothing in (which makes it ACTUALLY undefined) and then you avoid having some crackpot define the undefined globally.Morra
-1 because suggests window and "the global stack" (global scope?) are different.Disincentive
D
1

I think this is mostly for code that needs to run in multiple window contexts. Say you have a complex application with lots of iframes and/or child windows. They all need to run the code in MyObject, but you only want to load it once. So you load it in whatever window/frame you choose, but you create a MyObject for each window/frame with references to the proper window and document.

Taking an undefined argument is trying to protect against the fact that undefined can be changed:

undefined = 3;
alert(undefined);

See CMS's answer about how this improves safety.

Desmid answered 16/2, 2011 at 18:38 Comment(1)
It's not passing in undefined, it's just declaring a parameter named undefined, and not passing anything in for that parameter, which makes it actually undefined. Not sure why that's preferred over "var undefined" in the function, though.Consolidate

© 2022 - 2024 — McMap. All rights reserved.