Faraz Kelhini JavaScript developer.

What is globalThis, and why should you start using it?

5 min read 1657

What Is globalThis, And Why Should You Use It?

The JavaScript language is increasingly used in a wide variety of environments. In addition to the web browser, which is the most common type of host environment for JavaScript, you can run JavaScript programs in servers, smartphones, and even robotic hardware.

Each environment has its own object model and provides a different syntax to access the global object. In the web browser, for example, the global object is accessible via window, self, or frames. In Node.js, however, these properties don’t exist, and you must use global instead. In Web Workers, only self is available.

These different ways of referencing the global object have made it tough to write a portable JavaScript code that works in multiple environments. Fortunately, there’s a proposal in the works that aims to fix this issue by introducing a standard property called globalThis that will be available in all environments.

In this article, we’ll first look at the global object in popular JavaScript environments and then see how globalThis provides a unified mechanism to access it.

window

The window property is used to refer to the global object of the current document in the browser environment. At the top level of the code, variables declared using the var keyword become properties of window and are accessible from anywhere in the code:

var a = [10, 20];

console.log(window.a);          // → [10, 20]
console.log(a === window.a);    // → true

Normally, it’s not necessary to directly refer to window when using its properties because the reference is implied. However, when there is a local variable with the same name as the global variable, using window is the only option:

var a = 10;

(function() {
  var a = 20;   
  console.log(a);           // → 20
  console.log(window.a);    // → 10
})();

As you can see, window is very useful for referencing the global object, regardless of the scope in which the code is running. Note that window actually references window.window. So, window.window === window.

Besides the standard JavaScript properties and methods, the window object contains several additional properties and methods that allow us to control the web browser window as well as the document itself.

self

The Web Workers API doesn’t have a Window object because it has no browsing context. Instead, it provides the WorkerGlobalScope interface containing the data that’s normally carried by window.

To access the global object in Web Workers, we use self, which is a synonym for the window property of the Window object. Similar to window, self is a reference to the global object and can be used to make references explicit rather than implicit:

// a web worker
console.log(self);    // => DedicatedWorkerGlobalScope {...}

var a = 10;

console.log(self.a);          // → 10
console.log(a === self.a);    // → true

In a browser environment, this code would log Window rather than DedicatedWorkerGlobalScope. Because self‘s value changes depending on the environment in which it is used, it’s sometimes preferable to window. While self references WorkerGlobalScope.self in the web worker context, it references window.self in the browser context.

It’s important not to confuse the self property with the common JavaScript pattern of declaring a local variable, which is used to maintain a reference to a context. For instance:

const obj = {
  myProperty: 10,
  myMethod: function(){
    console.log(this === obj);    // => true

    // store the value of this in a variable for use in nested functions
    const self = this;

    const helperFunction = (function() {
      console.log(self === obj);  // => true (self refers to the outer this value)
      console.log(this === obj);  // => false (this refers to the global object. In strict mode, it has a value of undefined)
    })();
  }
};

// invoke myMethod on the object obj.
obj.myMethod();

frames

Another way to access the global object in the browser environment is to use the frames property, which works similar to self and window:

// browser environment
console.log(frames);    // => Window {...}

This read-only property is usually used to obtain a list of sub-frames of the current window. For example, you can use window.frames[0] or frames[0] to access the first frame.

global

In Node.js, you can access the global object using the global keyword:

// node environment
console.log(global);    // => Object [global] {...}

window, self, or frames won’t work in the Node environment. Keep in mind that the top-level scope in Node.js is not the global scope. In browsers, var abc = 123 will create a global variable. In Node.js, however, the variable will be local to the module itself.

this

In browsers, you can use the this keyword at the top level of your program to reference the global object:

this.foo = 123;
console.log(this.foo === window.foo);    // => true

this inside functions running in non-strict mode or arrow functions also references the global object. But that’s not the case with functions running in strict mode, in which this has a value of undefined:

(function() {
  console.log(this);    // => Window {...}
})();

(() => {
  console.log(this);    // => Window {...}
})();

(function() {
  "use strict";
  console.log(this);    // => undefined
})();

In Node modules, this at the top level doesn’t reference the global object. Instead, it has the same value as module.exports. Inside functions (Node environment), the value of this is determined based on how the function is called. In JavaScript modules, this at the top level is undefined.

Introducing globalThis

globalThis aims to consolidate the increasingly fragmented ways of accessing the global object by defining a standard global property. The globalThis proposal is currently at stage 4, which means it’s ready for inclusion in the ES2020 standard. All popular browsers, including Chrome 71+, Firefox 65+, and Safari 12.1+, already support the feature. You can also use it in Node.js 12+.

// browser environment
console.log(globalThis);    // => Window {...}

// node.js environment
console.log(globalThis);    // => Object [global] {...}

// web worker environment
console.log(globalThis);    // => DedicatedWorkerGlobalScope {...}

By using globalThis, your code will work in window and non-window contexts without having to write additional checks or tests. In most environments, globalThis directly refers to the global object of that environment. In browsers, however, a proxy is used internally to take into account iframe and cross-window security. In practice, it doesn’t change the way you write your code, though.

Generally, when you’re not sure in what environment your code will be used, or when you want to make your code executable in different environments, the globalThis property comes in very handy. You’ll have to use a polyfill to implement the feature on older browsers that do not support it, however.

On the other hand, if you’re certain what environment your code is going to be used in, then use one of the existing ways of referencing the environment’s global object and save yourself from the need to include a polyfill for globalThis.

Creating a globalThis polyfill

Prior to the introduction of globalThis, a common way to access the global object across different environments was to use the following pattern:

function getGlobalObject() {
  return Function('return this')();
}

if (typeof getGlobalObject().Promise.allSettled !== 'function') {
  // the Promise.allSettled() method is not available in this environment
}

The problem with this code is that the Function constructor and eval cannot be used in websites that enforce Content Security Policy (CSP). Chrome’s extension system also doesn’t allow such code to run because of CSP.

Another pattern to reference the global object is as follows:

function getGlobalObject() {
  if (typeof globalThis !== 'undefined') { return globalThis; }
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('cannot find the global object');
};

if (typeof getGlobalObject().Promise.allSettled !== 'function') {
  // the Promise.allSettled() method is not available in this environment
}

This pattern is commonly used on the web. But this too has several flaws, making it unreliable in certain situations. Fortunately, Mathias Bynens at Chrome DevTools has come up with a creative pattern that doesn’t suffer from those shortcomings:

(function() {
  if (typeof globalThis === 'object') return;
  Object.defineProperty(Object.prototype, '__magic__', {
    get: function() {
      return this;
    },
    configurable: true // This makes it possible to `delete` the getter later.
  });
  __magic__.globalThis = __magic__; // lolwat
  delete Object.prototype.__magic__;
}());

// Your code can use `globalThis` now.
console.log(globalThis);

This polyfill is a more robust solution compared to other approaches, but it’s still not perfect. Modifying Object, Object.defineProperty, or Object.prototype.__defineGetter__ could break the polyfill, as Mathias mentions.

Wrapping up

It’s difficult to write a portable JavaScript code that works in multiple environments. Each host environment has a slightly different object model. As a result, to access the global object, you need to use different syntax in different JavaScript environments.

With the introduction of the globalThis property, accessing the global object will become much simpler, and it will no longer be necessary to detect the environment in which the code is running.

At first glance, globalThis seems like an easy thing to polyfill; but in practice, it’s very complicated to do correctly. All existing workarounds are imperfect and might introduce bugs if you’re not careful.

ECMAScript is quickly evolving, and you can expect new features to be introduced more often. To get updated on the latest additions to the specs, check out the list of finished proposals.

Implementing new JS features? Understand How JavaScript errors affect your users.

Tracking down the cause of a production JavaScript exception or error is time consuming and frustrating. If you’re interested in monitoring JavaScript errors and seeing how they affect users, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. LogRocket enables you to aggregate and report on errors to see how frequent they occur and how much of your user base they affect. You can easily replay specific user sessions where an error took place to see what a user did that led to the bug.

LogRocket instruments your app to record requests/responses with headers + bodies along with contextual information about the user to get a full picture of an issue. It also records the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

Enhance your JavaScript error monitoring capabilities – – .

Faraz Kelhini JavaScript developer.

Leave a Reply