0

During the creation of a js class I've seen that window, document, Math, undefined are passed as arguments at the beggining and at the end of the class. Are they useful, and what do they mean?

var MyClass = (function (window, document, Math, undefined) {

  function MyClass (opts) {
    this.init(opts);
  }

  MyClass.prototype = {

    init: function (opts) {

    }

  return MyClass;
})(window, document, Math, undefined);
Luke
  • 2,976
  • 6
  • 34
  • 44

2 Answers2

1

You're missing a closing ending brace.

var MyClass = (function (window, document, Math, undefined) {

    function MyClass (opts) {
        this.init(opts);
    };

    MyClass.prototype = {

        init: function (opts) {

        }
    }; /* <-- closing brace here */

    return MyClass;

})(window, document, Math);

The globals, dependencies of the MyClass function/class, are passed into the method closure that's created by the anonymous wrapping function in order to mitigate the possibility of external scripts changing their intended behavior. This is a technique employed by the designers of the API to increase their reliability. One could imagine an instance of a MyClass function breaking, if someone executed the following script -- sometime after/before the MyClass function was defined:

Math = undefined /* or some other value */;

Thus, any code which depends upon Math being what its intended to be will suddenly break.

Driving it home


Aside from the aforementioned reason, local variables can be minified. Thus, reducing the size of the script that's eventually transmitted over the wire.

Alex
  • 34,899
  • 5
  • 77
  • 90
  • I think you're right, but of course it's pointless unless there's a guarantee that this code will run before any other code that might do the re–assignments. ;-) – RobG Oct 18 '13 at 01:45
  • it doesn't have to run before any other script, javascript is evented. any script that changes a global after the fact, will change the behavior hence forward ... – Alex Oct 18 '13 at 02:04
  • But a global may have been changed before the script runs, while this script wants the standard object. – Qantas 94 Heavy Oct 18 '13 at 02:13
  • @Qantas94Heavy it's ***mitigation*** not ***prevention***. you can't completely prevent people from blowing their own foot off ... – Alex Oct 18 '13 at 02:15
  • Well then why do that in the first place (apart from minification, which is valid reason to do so)? – Qantas 94 Heavy Oct 18 '13 at 02:16
  • @Qantas94Heavy because you've likely written a mature api and have come across people blowing their own feet off. thus, you write this defensive pattern to prevent *your* api from failing. – Alex Oct 18 '13 at 02:20
  • Well then what about `Math.atan2 = function (){}`? It _doesn't_ prevent people blowing their feet off. – Qantas 94 Heavy Oct 18 '13 at 02:21
  • @Qantas94Heavy that's not going to break your code cause you wouldn't call `Math.atan2` inside `MyClass` ... – Alex Oct 18 '13 at 02:23
  • But if nothing depended on a `Math` function, why do so in the first place? – Qantas 94 Heavy Oct 18 '13 at 02:30
  • @Qantas94Heavy `Math` is an argument to `MyClass`, thus `MyClass` depends on the `Math` API. this pattern is in the jQuery source. – Alex Oct 18 '13 at 02:31
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/39458/discussion-between-qantas-94-heavy-and-xander) – Qantas 94 Heavy Oct 18 '13 at 02:49
0

The use of:

var MyClass = (function (window, document, Math, undefined) {
 ...
})(window, document, Math, undefined);

is misguided. If the intention is to get "safe" access to native methods, then it will fail if those identifiers have already been assigned new values before the code runs.

The best you can do is:

var myClass = function(global) {

  // In here you can be certain that global references the global (window) object
  var window = global; 

  // and that the value of undefined will be undefined
  var undefined;

  // But you are still uncertain of
  var document = window.document;
  var Math = window.Math;

  ...

// guaranteed access to the global object
}(this));

The value of this can't be overwritten in any context (though it can be set on entering a function by the call or with bind), so in the global execution context it must reference the original global object. But the Math and window properties may have been reassigned, you can't stop that (though a future version of ECMAScript may make it impossible in certain circumstances).

So if the IIFE must run before the properties of interest have been reassigned, then creating aliases does nothing of value. Though as others have said, it may help in minification.

Note that creating aliases of host objects and methods is problematic, e.g.

var getEl = document.getElementById;
var el = getEl('foo');

may well throw an error as the getElementById function may expect to be called as a method of document and therefore its this may not be set correctly.

RobG
  • 142,382
  • 31
  • 172
  • 209