ES proposal: global

[2016-09-29] dev, javascript, esnext, es proposal
(Ad, please don’t block)
Warning: This blog post is outdated. Instead, read section “globalThis in “JavaScript for impatient programmers”.

The ECMAScript proposal “global” by Jordan Harband is currently at stage 3. It provides a new standard way of accessing the global object.

Referring to the global object  

The following are a few popular ways of referring to the global object:

  • Global variables:
    • Global variable window: is the classic way of referring to the global object. But it doesn’t work in Node.js and in Web Workers.
    • Global variable self: is available in Web Workers and browsers in general. But it isn’t supported by Node.js. Some people take self appearing in code, as a sign that that code works in both Web Workers and normal browser settings.
    • Global variable global: is only available in Node.js. Until now!
  • this:
    • this in global scope: refers to the global object. The only problem is that Node.js modules and ES6 modules have their own scopes, which means that this approach doesn’t work there.
    • this during a function call in sloppy mode: If you call a function via a function call (and not a method call), its this refers to the global object in non-strict mode. In strict mode, it is undefined.
    • new Function('return this')(): works in both strict mode and sloppy mode, because the parameters of new Function() are always evaluated in sloppy mode. There is one important caveat: eval, new Function(), etc. are not available if you use CSP (Content Security Policy). That makes this approach unsuited in many cases.

The proposal  

The ECMAScript proposal standardizes the global variable global for accessing the global object. It also standardizes that the global object must have Object.prototype in its prototype chain. The following is already true in web browsers today:

> Object.prototype.isPrototypeOf(window)
true

Best practices  

The global object is now considered a mistake that JavaScript can’t get rid of, due to backward compatibility. It affects performance negatively and is generally a confusing feature.

ECMAScript 6 moves away from the global object by providing three new ways for declaring variables that don’t create global properties in global scope (as var declarations and function declarations do):

  • let declarations
  • const declarations
  • Class declarations

In other words: all properties of the global object are global variables, but not all global variables are properties of the global object. For example (executed in global scope):

> var foo;
> 'foo' in window
true

> let bar;
> 'bar' in window
false

It is normally preferable to refer to global variables as variables and not as properties of, e.g., window. That has always worked on all JavaScript platforms.

Furthermore, starting with ES6 (and even before), most JavaScript code lives in modules and will thus never be in global scope.

Therefore, global will mostly be relevant for polyfills.

A polyfill  

The proposal’s author, Jordan Harband, has written a polyfill for it.

Using it with CommonJS syntax:

// Computing the value of `global`
var global = require('system.global')();

// Shimming `global` (installing it globally)
require('system.global/shim')();

Using it with ES6 module syntax:

// Computing the value of `global`
import getGlobal from 'system.global';
const global = getGlobal();

// Shimming `global` (installing it globally)
import shim from ‘system.global/shim’; shim();

The package always uses the “most native” approach available (global on Node.js etc., window in normal browser contexts, etc.).

Computing a reference to the global object  

Internally, the polyfill uses the function getPolyfill() to compute a reference to the global object. This is how that is achieved:

// polyfill.js
var implementation = require('./implementation');
module.exports = function getPolyfill() {
    if (typeof global !== 'object' || !global
        || global.Math !== Math || global.Array !== Array) {
        return implementation;
    }
    return global;
};

// implementation.js
if (typeof self !== 'undefined') {
    module.exports = self;
} else if (typeof window !== 'undefined') {
    module.exports = window;
} else if (typeof global !== 'undefined') {
    module.exports = global;
} else {
    module.exports = Function('return this')();
}

More information on the global object