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; usingthis
(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 = ...
(substitutingthis
withwindow
orglobal
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