开发者

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

This question already has answers here: How does this JavaScript/jQuery syntax work: (function( window, undefined ) { })(window)? 开发者_如何转开发 (5 answers) Closed 8 years ago.

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?


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.


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.


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜