DEV Community

Cover image for Beyond window: Understanding global and globalThis in JavaScript
Saumya Agrawal
Saumya Agrawal

Posted on • Originally published at blog.saumyagrawal.in

Beyond window: Understanding global and globalThis in JavaScript

When you wrote your first code in Node.js, you used console and setTimeout without the need for import or declaration. They are present in your IDE, ready to be used. Have you ever wondered where they came from?

It is all the work of global, an object that Node.js creates way before the code even starts to run. An object that holds everything the runtime gives you for free.

JavaScript then introduced globalThis, which is a story in itself about the growth of Node.js.


What Is Node.js?

Before we dive into global, let's understand what Node.js is.

JavaScript was initially intended for browsers to run inside Chrome, Firefox, and Safari. The browsers provided a runtime, an engine to execute the code, along with a set of built-in tools like document, window, and fetch.

Node.js wanted to take the JavaScript engine, V8, out of the browser and make it run anywhere.

Difference in Browser and Node.js

The same language running on a different environment, but they have their own version of an object that holds everything a code needs without the need to import.

In the browser: the object is window.

In Node.js: the object is called global.


global: The Node.js Global Object

When Node.js starts, it creates a global object and provides it with every tool the code needs.

// When the code is written:
console.log("I am global");
setTimeout(() => {}, 1000);

// Javascript does this in the background:
global.console.log("I am global");
global.setTimeout(() => {}, 1000);
Enter fullscreen mode Exit fullscreen mode

But Node.js allows us to skip adding global every time, and looks things up on global automatically.

console.log(global.console === console);       // true
console.log(global.setTimeout === setTimeout); // true
Enter fullscreen mode Exit fullscreen mode

They are the same objects, global is where they are stored.

What lives on global

Web API native to global and Node

this in Node.js

// At the top of a Node.js file:
console.log(this);          
console.log(this === global); 
Enter fullscreen mode Exit fullscreen mode

By looking at the code above, you might expect this to be global. Surprisingly, it is not. The output of console.log(this) is {} which is not global.

Node.js wraps every single file in a function before running it:

//  Node.js silently wraps your file in this
(function(exports, require, module,                     
            __filename, __dirname) {                      

    // everything you write goes in here                  

  }); 
Enter fullscreen mode Exit fullscreen mode

The code is not at the true top level, but it is inside a function. And this inside that function points to module.exports (which starts as {}), not to global.

console.log(this); // {}
console.log(this === global); // false
console.log(this === module.exports); // true
Enter fullscreen mode Exit fullscreen mode

In Node.js, this returns global when a function is called.

function test() {
  console.log(this);    // Object [global].... (in not-strict mode)
}

test();

// in browser
console.log(this); // window
console.log(this === window); // true
Enter fullscreen mode Exit fullscreen mode

this reference note:

Where code runs What this becomes
Browser top-level window
Node top level module.exports
Function call (non-strict) global object
Strict mode function undefined
Method call the object
Every environment global globalThis

The Problem That Needed Solving

For years, Node.js developers just used global and browser developers used window. The problem here was the need to access global object in both environments. If you build a utility library that formats dates, handles errors, or adds a bit of logging, and publish it on npm so both browser apps and Node.js apps can use it, it would be difficult because of the different global object.

// This crashes in Node.js as window does not exist here
console.log(window.Math);
// ReferenceError: window is not defined

// This crashes in a browser as global does not exist here
console.log(global.Math);
// ReferenceError: global is not defined
Enter fullscreen mode Exit fullscreen mode

Neither name works in both places. So developers started writing workarounds:

// The only solution was:
const globalObject =
  typeof window !== "undefined" ? window :
  typeof global !== "undefined" ? global :
  typeof self   !== "undefined" ? self   :
  null;
Enter fullscreen mode Exit fullscreen mode

This solved the problem of global. Hence, there came a need to introduce a fix.

globalThis

ES2020 introduced globalThis as the standard, universal way to access the global object in any JavaScript environment.

// Works in Node.js:
console.log(globalThis === global); // true

// Works in a browser:
console.log(globalThis === window); // true

// Works in a Web Worker:
console.log(globalThis === self);   // true
Enter fullscreen mode Exit fullscreen mode

global object in different environment

Hence, the solution the devs used was replaced by a single line of code

const globalObject = globalThis;
Enter fullscreen mode Exit fullscreen mode

globalThis did not replace global in Node.js. global is still there and valid Node.js code. But globalThis is now the recommended way, especially in the code that might run outside of Node.js.


global vs globalThis

// These do exactly the same thing in Node.js
console.log(global.Math.PI);      // 3.141592653589793
console.log(globalThis.Math.PI);  // 3.141592653589793

// These are the same object
console.log(global === globalThis); // true

// Both let you access anything on the global scope
console.log(global.setTimeout === setTimeout);      // true
console.log(globalThis.setTimeout === setTimeout);  // true
Enter fullscreen mode Exit fullscreen mode

The difference is:

global works globalThis works
Node.js Yes Yes
Browser No Yes
Workers No Yes
Deno No Yes

globalThis is used when a package, utility, or library is created that is meant to be shared.


Do Not Add Things to global Yourself

Node.js will not stop you from putting your own values on global. However, it is not recommended to use them, and the following example illustrates why.

If you had a small application that required a username and were about to use that username in multiple files throughout the application, you might think that it would be beneficial to place the username into a global variable so that you can reference it throughout the entire application.

// app.js — your main file
global.username = "User";

console.log("App started");


// greet.js — a separate file
function greet() {
  console.log("Hello, " + username); // no import, just works
}

greet(); // "Hello, User"
Enter fullscreen mode Exit fullscreen mode

As your application grows and you eventually have ten files, then there is a potential that after months of work on your application, you will have a situation wherein you will have printed a username incorrectly and upon opening the file where the erroneous user name was printed (greet.js), you would notice there was no import statement for this username, thus you would be required to search through all of the files to locate the source of the incorrect username.

This is what makes variables defined as global variables invisible, and therefore, there is no trail leading to their origin.

When done correctly:

// user.js: one place where username lives
const username = "User";

module.exports = username;

// greet.js: it's clear exactly where username comes from
const username = require("./user");

function greet() {
  console.log("Hello, " + username);
}

greet(); // "Hello, User"
Enter fullscreen mode Exit fullscreen mode

Hence, if instead of globally defining your username, you had created an import statement for the username and used the import in greet.js to define the username, then anyone who looks at greet.js immediately sees where the username is declared, and there is no need to search. This type of coding is much easier to work with, especially when the size of your application increases, or someone new to the project is introduced.


JavaScript was originally designed to run on the browser. However, Node.js took it outside the browser environment and runs JavaScript without the need for a browser.

The way to access that global object was through the variable global. Likewise, browsers have a built-in global variable called window and Web Workers have a built-in global variable called self, which performs the same function as the global variable.

As JavaScript expanded into other environments, developers wanted to write code that worked across all of those environments, like Node.js, Windows, etc., with fewer modifications. However, because there was no consistent name for the global object, this was difficult for developers.

Fortunately, ES2020 introduced the globalThis variable as the global reference for JavaScript in all environments, it will point to the same object no matter what environment your JavaScript code runs in.

In the case of Node.js, global and globalThis both point to the same place. You would use global if you wanted to write code that was specific to Node.js and you want to explicitly reference global. You would use globalThis if you needed to write code that may eventually run in a different environment.


References

Top comments (0)