When I started programming one of the first things that I learned was that I should avoid the usage of global variables whenever possible. It’s known as a bad practice in programming because it pollutes the global namespace and is not safe to work with. As a result, scripts may interfere with each other, which can lead to unexpected behaviour and hard to find bugs.
Thinking of front-end code, it’s clear that additional global variables do have a strong use case. Libraries like jQuery place themselves in the global namespace to make their use as easy as possible by just adding a script element to an HTML page.
It’s common practice to use an IIFE (immediately invoked function expression) to prevent variables leaking into the global scope. This IIFE is then executed with the
window object to set new properties on it.
frames), and for the context of Node.js we can use
To write a test in Mocha you have to use the
it function. The testing framework places these functions in the global object for you ready to use. The Mocha source code is initially written for the Node.js context which means that the accessible global object is
So what does it take to make this code runnable in the browser context, too?
Mocha uses Browserify to build an additional file that can run in the browser context. The build process wraps the code in an IIFE and provides an object named
For the sake of simplicity let’s look a simpler example that does nothing else than setting a
foo variable to the global scope running in Node.js context.
I don’t know about you, but this is nothing I’d call beginner friendly or easy to read. Do we really need that many checks to figure out what the global object in an environment is?
So let’s look at the generated browserify output again.
This code looks one after another for the properties
window to be present. If none of them is defined, it gives up and just assigns a new plain object. This evaluation covers the traditional browser environment, service and web workers and the Node.js context.
I hear you say that
this also refers to the global object (at least sometimes). So why can’t we go with an IIFE and passing
this to it?
That’s right! This snippet works, but only if this snippet is not nested inside of another function. Because then
this might refer to a changed context or even be
undefined (code running in strict mode).
this at top level inside of a module won't reference the global object but rather be
undefined (thanks to Axel Rauschmayer for pointing that out).
So what other options do we have?
Functions that have been created using the function constructor always run in the global scope. This fact ensures that we’re dealing with the global scope and then using
this becomes a safe way to retrieve the current global object.
This snippet works in strict mode, inside or outside of functions and is probably the best bet we have.
The big downside with the function constructor is that Content Security Policy directives will prevent its execution. CSP helps to reduce the risk of XSS attacks and is a useful technology, but unfortunately using function constructors falls into the category of “unsafe dynamic code evaluation”. So when we want to use function constructors we have to allow dynamic code evaluation and this is most likely something we don't want to do.
So in the end, it turns out that there is currently no silver bullet to retrieve the real global object in all possible environments. The function constructor is the most accurate one, but it’s not guaranteed that code using it won’t be blocked by CSP directives.
Daniel Ehrenberg had the same feeling and came up with a proposal to make the global object easily accessible to get rid of all these evaluations.
Everybody seemed to like this idea, and the proposal is currently on stage 3 of the TC39 process. One thing that may need further discussions is the actual name of the property that should hold the reference. Most people agreed with
global similar to the Node.js environment.
At the time of writing, people are validating if this addition may have any negative impact on the web platform itself. Do you remember the drama about Array.prototype.contains? The web doesn’t forget code that was pushed out there. New language features have to be evaluated carefully to be sure that additions and changes are not breaking existing websites.
In fact, it turns out that adding the property
global breaks Flickr and Jira, which probably means that the proposal has to be changed to use a different property name. Discussions about using
System.global started already.
So let’s hope for the best, because even when we try to avoid the usage of globals, there are use cases for them and these shouldn't need the usage of a heavily nested ternary operator that no one understands.