Perfection Kills

by kangax

Exploring Javascript by example

← back 3748 words

Unnecessarily comprehensive look into a rather insignificant issue of global objects creation

I noticed this code few days ago — in one of the “modules” of an internal app I’m working on — which looked like this:


if (MyApp == null) {
    var MyApp = {}
}

This was at the beginning of the file, and was obviously a way to define global “namespace” object, if one didn’t exist. The condition in this particular case, however, looked rather unusual — if (MyApp == null). It got me thinking about the ridiculous number of ways in which it is possible to define a global object in Javascript.

I’m sure you’ve seen the variations of this kind:


if (typeof MyApp == 'undefined') {
  var MyApp = { };
}

or:


if (!window.MyApp) {
  window.MyApp = { };
}

or maybe even something as strange as:


if (MyApp === undefined) {
  MyApp = { };
}

But have you ever wondered about the difference between these? Are any of them “better” than the other ones, and why? Is there a particular way you prefer, and for which reason?

If we were to look into some of the possible global object creation patterns, it turns out there’s quite a bit of interesting little details (and traps) here and there. What do you mean they’re not interesting? Of course they are! :)

As a fun exercise, let’s go over some of the more popular variations, explaining the inner workings of each one of them. We’ll take a look at troublesome ones; those that are more compatible than others; and those that differ depending on whether implementation follows ES3 or ES5 rules. Hopefully, you’ll also learn a thing or two about ECMAScript while we’re at it.

How many ways to skin a cat?

So what are the options for creating a global object if one doesn’t exist? Well, let’s see. There are conditions, used as expression in an if statement. And there are actions used as the inner statement in an if statement.


if (condition) {
  action
}

Even if we’re not using if statement, the general pattern of condition + action is still in effect:


condition && action
!condition || action
// etc.

The lists of conditions and actions might look like this (I’ll be using MyApp “name” as an example):

Conditions:


  !MyApp
  !window.MyApp
  !this.MyApp
  !global.MyApp
  typeof MyApp == "undefined"
  typeof MyApp != "object"
  MyApp === undefined
  MyApp == null
  "MyApp" in window
  window.hasOwnProperty("MyApp")

Actions:


  var MyApp = { }
  window.MyApp = { }
  this.MyApp = { }
  global.MyApp = { } // where `global` references global object
  MyApp = { }

There are undoubtedly other similar variations, such as using bracket notation instead of dot notation — if (window['MyApp']) { ... }, or more obscure ones like eval('var MyApp = ...'), but we’ll omit those for the sake of brevity. Arguably, some of the items in this list are rather similar to each other, like window.MyApp, this.MyApp, and global.MyApp — after all, they are all assignments to a property of global object — but I’d like to show the important difference in each one of them, which is why we’ll look at them separately.

I promise not to go over all of the 50+ possible combinations. Only a handful of them.

Patterns overview

1) typeof + variable declaration


if (typeof MyApp == 'undefined') {
  var MyApp = { };
}

This is probably the most popular variation. Good old variable declaration, and a typeof check. When executed as global code, it creates a global variable (if one doesn’t exist) and assigns an object reference to it. What’s important to understand about this snippet is that variable declaration — similar to function declaration — follows the so-called “hoisting” behavior. In other words, MyApp variable is created before any statements in the current scope are executed. This is why the snippet is functionally identical to:


var MyApp;
...
if (typeof MyApp == 'undefined') {
  MyApp = { };
}

When MyApp is already defined and references something, typeof MyApp != 'undefined' check does not succeed and MyApp is not overwritten. The declaration of MyApp does nothing since binding already exists in this scope. However, if MyApp doesn’t yet exist, typeof MyApp evaluates to “undefined”, and MyApp is assigned a reference to a newly created object. The reason typeof MyApp evaluates to “undefined” is because at that point MyApp is already declared and has an undefined value (as all undeclared variables do).

2) boolean conversion + variable declaration


if (!MyApp) {
  var MyApp = { };
}

Another common version is the same as the previous one, only with typeof check replaced by a boolean conversion one. This one is clearly shorter but is it just as reliable? I remember being confused about it a while back. Shouldn’t MyApp throw a ReferenceError if MyApp isn’t declared — I thought. My fears were certainly unfounded. The “hoisting” behavior of variable declarations prevents ReferenceError from occurring here. Similarly to the previous example, let’s see a functionally identical version of a snippet:


var MyApp;
...
if (!MyApp) {
  MyApp = { };
}

var MyApp that’s inside the block, hoists MyApp declaration to the top of the scope. By the time execution gets to the if statement, MyApp is already declared and has an undefined value. if (!MyApp) can never throw a ReferenceError, since the variable is always declared.

3) boolean conversion + undeclared assignment


if (!MyApp) {
  MyApp = { };
}

Sometimes a perfectly good second version is taken to the “next level” with this unpleasant variation. Unlike previous example, there’s no variable declaration here; only an assignment. When MyApp doesn’t exist, !MyApp expression throws ReferenceError for exactly this reason — the variable is never declared, and the access to it should throw! This version is hardly practical, but it’s good to understand why it doesn’t “work”.

Undeclared assignment

Before we move further, take a closer look at MyApp = { } line. Anything looks suspicious there?

This is a so-called undeclared assignment. Instead of declaring a variable via a variable statement (i.e. var MyApp = { }), MyApp is being assigned a value directly. In ECMAScript, when something is being assigned to non-existing variable, the variable with that name is created on a global object and becomes available to any code. Well, to be more precise, it becomes a global property, rather than a variable, but this subtle difference is not important now.

So MyApp = { } creates a global MyApp property, then assigns a newly created object reference to it.

Note how very much MyApp = { } is different from var MyApp = { }. The former one — undeclared assignment — creates a global property, whereas latter one — variable declaration — creates a local variable.

Undeclared assignments are historically considered a bad practice. They lead to confusion and increase the risk of global collisions. There are also certain browser bugs, such as that in IE (MSHTML DOM), where undeclared assignment results in an error if element with same id/name exists in the document. There’s a big chance for beginner to forget var and end up creating an unwanted global property. Or simply consider var’less version to be similar to that with the var. Or not even know about having to use var — after all, plain assignment seems to work just fine.

Since undeclared assignments are such a misleading part of the language, ECMAScript 5 — newest version of ECMAScript — specifies that when they occur in a strict mode, a ReferenceError should be thrown. This makes for more robust code with less chance for unexpected errors. And since strict mode is more or less a future direction of the language, it makes sense to avoid undeclared assignments as if they never existed.

4) typeof + undeclared assignment


if (typeof MyApp == 'undefined') {
  MyApp = { };
}

Another harmful variation, and unfortunately the one that actually “works”. Note how there’s still an undeclared assignment, but the condition is now replaced with a typeof check. typeof check is exactly what makes all of this work, unlike plain variable access in the previous example. In ECMAScript, when typeof is passed an undeclared identifier, it’s specified to return string “undefined”. This kind of leniency exists on purpose — typeof is meant to be used with undeclared identifiers; it doesn’t throw (as regular variable access does) but returns “undefined” instead.

So when MyApp doesn’t exist and if expression succeeds, MyApp = { } is evaluated and creates a global MyApp property. The problem here is that MyApp = { } is still an undeclared assignment and so has all the same problems as undeclared assignment from the previous example.

5) property access on window


if (!window.MyApp) {
  window.MyApp = { };
}

Stepping away from variable declarations and undeclared assignments, which other ways are there to create a global property?

Well, by assigning directly to a global object of course. In browsers global object is often referenced through global window property. So one of the ways to create a property on a global object is by assigning to property of window. This solution is almost perfect, except for two drawbacks.

First, it makes code less portable, as there is a reliance on presence of global window property. If you wanted to run it in a non-browser environment (V8, Rhino, WScript, etc.) window is likely to be non-existent there.

Second problem is a bit more vague. It’s relevant to browser environments only, and it’s about window not referencing global object directly. HTML5 defines window to reference not global object, but so-called WindowProxy object. Even though all operations performed on WindowProxy must also be performed on the Window object, there are few existing quirks — notably in Internet Explorer — where global (variable) object and window are not fully interchangeable (more evidence).

Curiously, current HTML5 draft also says that window typically references a Global Object, which makes it a non-requirement. In practice, however, window almost always references Global Object (or at least an object that acts as if it was a global object).

6) property access on this

If you don’t want to rely on browser-only window, the most straightforward way to assign to a global object is by creating a reference that definitely gives us a Global Object. How to create such reference? Well, since we’re in global code, we can freely use this keyword.


if (!this.MyApp) {
  this.MyApp = { };
}

This works beautifully, and should create a global object in any respectable (i.e. ECMAScript-compliant) implementation. Note, however, that this code can only be run in global scope to ensure that this references global object. Of course you could also run it non-globally as long as this keeps referencing global object:


var myObject = {
  myMethod: function() {
    ...
    if (!this.MyApp) {
      this.MyApp = { };
    }
  }
};

// call method with `this` referencing global object rather than `myObject`

myObject.myMethod.call(this);

The requirement to be able to create global object from within non-global code might seem contrived. Yet, I’ve seen quite few times how javascript files — representing different “modules” — were concatenated together and wrapped into another, global self-executing function. Or the code would be eval’d after being retrieved from the server via XHR. During evaluation, it would be executed in the scope of a caller, not in the global scope.

This kind of “global object passing” could certainly become cumbersome. If only there was a way to get access to global object from within any scope. Well, there certainly is.

7) property access on global

The easiest way to achieve this is by creating a globally-accessible unique property referencing global object:


// from within global code
var global = this;

.. which brings us to the next variation of global object creation:


if (!global.MyApp) {
  global.MyApp = { };
}

This snippet could now be executed from anywhere — as long as global binding is not shadowed by anything in the executing scope.


(function(global){

  // this obviously won't work since `global` is now a local variable and has a value of `undefined`.

  if (!global.MyApp) {
    globa.MyApp = { };
  }

})();

We solved the problem of not being able to run code from anywhere, but at the expense of having an extra global property. We’ve also introduced a requirement to actually create that global property. If our code is run in unknown environment, where it’s not possible to create this global object shortcut, this kind of solution is out of the question. Additionally, there’s a chance of collisions if another code defines global global property referencing something other than global object.

This is disastrous. What to do?

Global object retrieval

In ECMAScript 3 — it turns out — there’s a very easy way to retrieve global object from within any code. The beauty of the following solution is also that it’s not affected by local shadowing. It’s pretty much the perfect way to get access to global object:


var global = (function(){return this})();

The idea is simple. In ES3, when function is called as a function (not as a method; more precisely — as a non-reference or as a reference with null base object) its this is set to reference global object. When (function(){ ... })() is executed, it evaluates to a non-reference, so it’s invoked with this referencing global object.

Alternatively, we could have created a reference with null base object, which would also execute function with this referencing global object:


function f(){return this}
var global = f();

.. but that wouldn’t be as short and concise.

Great! We can now have global object from within anywhere. Is there a catch?

ECMAScript 5 strict mode

Well, yes, kind of. The catch is ECMAScript 5 and its newly-introduced strict mode. In ES5 strict mode, this no longer references global object in the above two cases:


"use strict";
(function(){return this})(); // undefined

function f(){return this}
f(); // undefined

The rationale for this was, supposedly, to increase security by limiting access to global object. There is, however, a workaround and it involves indirect eval call (which I explained in the past):


"use strict";
var global = (1,eval)("this");

Indirect eval evaluates code in global scope, and MUST set this to reference global object. So (1,eval)('this'), which is an indirect eval call, should evaluate to a global object.

The downside of this workaround is potential shadowing of eval by something other than built-in eval. The solution is certainly not as robust as (function(){return this})() one, which can be used in ES3 and ES5-non-strict code.

So to summarize:


var global = (function(){return this})(); // ES3, ES5 non strict
var global = (1,eval)('this'); // ES5 strict

In our current world of just-appearing ES5-compliant implementations, it’s hard to come up with a reliable cross-platform solution. Should we use ES3-ES5-non-strict snippet or ES5-strict one? The latter one sounds like a more future-proof solution, but reliance on indirect eval makes it a moving target: as we’ve seen before, indirect eval behavior varies across browsers.

Perhaps we can assume that browsers with strict mode support also follow ES5-compliant indirect eval behavior? In that case solution is simple:


"use strict";
var global = (function(){ return this || (1,eval)('this') })();

Implementations that don’t support strict mode, should evaluate left-hand side of || expression — this, which would be a global object and as any object be truthy. Implementations that DO support strict mode, should evaluate right-hand side of an expression since this would be undefined. Right-hand side should then evaluate to a global object — if eval still references built-in function of course.

This solution is arguably too verbose so it might not be desirable in all cases.

8) property access + variable declaration

Another common pattern is the one that combines any of the previously discussed solutions. For example, property check as condition and variable declaration as action:


if (!window.MyApp) {
  var MyApp = { };
}

or typeof check as condition and property creation as an action:


if (typeof MyApp == 'undefined') {
  window.MyApp = { };
}

Are there any problems with this variation? Benefits?

Well, first version can obviously only work when run from within global code; we need variable declaration to create global binding. The potential problem with this snippet is that we check existence of property on window but create it on global object. If window doesn’t reference global object, there’s a room for discrepancy. We could use previously discussed this or global instead of window — to refer to a global object:


if (!this.MyApp) {
  var MyApp = { };
}

In this case, the snippet is not much different from the first example:


if (typeof MyApp == 'undefined') {
  var MyApp = { };
}

The only subtle difference here is that this.MyApp check proceeds when this.MyApp evaluates to something falsy (0, ”, NaN, null, undefined, false), whereas typeof MyApp == "undefined" — only when MyApp evaluates to undefined:


var MyApp = '';
...

if (!this.MyApp) {
  // this statement is executed; MyApp is overwritten
  var MyApp = { };
}
if (typeof MyApp == 'undefined') {
  // this statement is not executed; MyApp is not overwritten
  var MyApp = { };
}

Since both this.MyApp = { } and var MyApp = { } will create global MyApp binding, the only difference here is that former one makes it deletable, while latter one doesn’t. Other than that, there is no difference — both operate on a global object when run from within global code. At least in theory. In practice, though, there has been some evidence of IE having different objects as global variable object and window object. For best cross-browser results, I would suggest to avoid mixing them up like this.

9) typeof “object”

Sometimes typeof MyApp == 'undefined' check is replaced with the opposite one — typeof MyApp != 'object'.


  if (typeof MyApp != 'object') {
    ...
  }

There isn’t much difference here, of course, except that typeof != 'object' check will also catch any other non-object and non-undefined values; pretty much all primitives except undefined:


  var MyApp = "trolololololo";

  if (typeof MyApp != 'object') {
    MyApp = { }; // MyApp is overwritten
  }

10) == null and === undefined

Other times, typeof check is replaced with direct comparison with undefined or null values:


if (MyApp === undefined) {
  var MyApp = { };
}

// or

if (MyApp == null) {
  var MyApp = { };
}

The second example is what I mentioned in the beginning and what got me surprised in the first place. As you probably know, comparing to null via equality operator (==) returns true when value is either null or undefined. This is why it allows to catch undeclared variables which have undefined values.

If you think about it, there’s really no reason to use this kind of check though. Even though it’s shorter than typeof, comparing to undefined is more fragile, since undefined is a writable property in ES3 (which is why — without taking precautions — it has been generally recommended against). Comparing to null is safer but is still unnecessary; !MyApp is just enough and is even shorter.

11) in and hasOwnProperty

There are even more exotic beasts such as those using in operator or hasOwnProperty method.


if (!('MyApp' in this)) {
  this.MyApp = { };
}

if (!this.hasOwnProperty('MyApp')) {
  this.MyApp = { };
}

Using in is almost identical to using plain boolean conversion — if (!MyApp) .... The only difference is that in will “catch” global properties with any values, whereas boolean conversion-based one — only those that have truthy values. Similarly to typeof ... == "undefined" check, in will catch both — undeclared variables and variables with undefined values.

hasOwnProperty, on the other hand, is a bit more strict. Even though irrelevant in our case, it’s worth remembering that hasOwnProperty will catch only those properties that exist directly on a global object. That means nothing from global object’s prototype chain, which could include Object.prototype, Window.prototype, etc.


this.hasOwnProperty('localStorage'); // false
'localStorage' in this; // true

this.hasOwnProperty('alert'); // false
'alert' in this; // true

this.hasOwnProperty('toString'); // false
'toString' in this; // true

var x;
this.hasOwnProperty('x'); // true
'x' in this; // true

12) || operator


global.MyApp || (global.MyApp = { });
this.MyApp = this.MyApp || { };
typeof MyApp == 'undefined' && (global.MyApp = { });

A slightly different way to write #5, #6, or #7 is using || operator. The functionality is the same; this is only a matter of preference.

Takeaway points

So there you have it. A quick rundown through some of the most popular options. To summarize:

  • Avoid undeclared assignments. They are a “dying” part of the language.
  • Assigning to window is not completely cross-platform; using this (or retrieving global object) is more portable.
  • Retrieving global object is iffy. You can use (function(){ return this || (1,eval)('this') })() which should work in ES3, ES5 and ES5-strict.
  • Some variations (e.g. variable assignment) work only in global scope; others (e.g. assignment to window) — from within any.
  • No need to perform typeof "undefined", or === undefined checks; plain ! operator works just as well, as long as variable is created via declaration.
  • When in global scope, the easiest & simplest version to create global object is if (!MyApp) var MyApp = ...
  • When object needs to be deleted at some point after creation, property assignment is the way to go: if (!this.MyApp) this.MyApp = ... (substituting this with window or global as necessary)
  • Avoid mixing this/window/global and global variable object (used to create property during variable declaration); they could be different objects.

Did you like this? Donations are welcome

comments powered by Disqus