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.
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);
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
They are the same objects, global is where they are stored.
What lives on global
this in Node.js
// At the top of a Node.js file:
console.log(this);
console.log(this === global);
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
});
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
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
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
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;
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
Hence, the solution the devs used was replaced by a single line of code
const globalObject = globalThis;
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
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"
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"
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.



Top comments (0)