W3docs

JavaScript globalThis and Global Object

Learn the JavaScript global object: globalThis vs window vs global, what lives on it, why top-level declarations leak onto it, and how to avoid polluting it.

Every JavaScript program runs inside one top-level object that exists before your code starts: the global object. It holds the language's built-ins (Array, Math, JSON, setTimeout, and so on) and is the implicit home of anything you declare at the very top of a classic script. This page explains what the global object is, how to reach it portably with globalThis, what actually lives on it, why some top-level declarations "leak" onto it while others don't, and how to keep your own globals under control.

What the global object is

The global object is the root of the scope chain. When you reference a name that isn't found in any enclosing function or block scope, the engine finally looks for it as a property of the global object. That's why Math.max or JSON.parse work anywhere — they are properties of the global object that the runtime sets up for you.

Each runtime exposes that object under a different name, which historically made cross-environment code awkward:

  • Browsers call it window (and also expose self, plus frames).
  • Web Workers call it self (there is no window in a worker).
  • Node.js calls it global.

globalThis: the portable global

Because the name differs per environment, ES2020 added globalThis — a single standard reference that points at the global object everywhere: browsers, workers, Node.js, Deno, and more. Reach for it whenever you need the real global object in code that must run in more than one place.

console.log(typeof globalThis); // "object" in every environment

// Each of these is true only in its own environment:
// globalThis === window  -> true in a browser tab
// globalThis === self    -> true in a browser or a Web Worker
// globalThis === global  -> true in Node.js

Trying to use the wrong name throws a ReferenceError, which is exactly the problem globalThis solves:

javascript— editable

For the bigger picture of what a host environment adds on top of the language, see the browser environment and specs.

What lives on the global object

The global object carries two kinds of things:

  1. Language built-ins, defined by the ECMAScript spec and present everywhere: Object, Array, Function, String, Number, Boolean, Symbol, BigInt, Math, JSON, Date, RegExp, Promise, Map, Set, the error constructors, and global functions like parseInt, isNaN, and eval.
  2. Host (environment) APIs, added by the runtime: document, fetch, localStorage, setTimeout, and alert in the browser; process, Buffer, and require in Node.js.
// Built-ins are reachable through the global object:
console.log(globalThis.Math.max(2, 7, 4)); // 7
console.log(globalThis.JSON.stringify({ ok: true })); // {"ok":true}

Why top-level var and functions leak onto it

In a classic (non-module) browser script, a var declared at the top level and a top-level function declaration both become properties of window. This is legacy behavior baked into the language for backward compatibility:

// In a classic browser script (not a module):
var greeting = 'hi';
function greet() { return greeting; }

console.log(window.greeting);     // "hi"
console.log(typeof window.greet); // "function"

Block-scoped declarations behave differently. let, const, and class at the top level create global bindings but do not attach to the global object:

let count = 1;
const name = 'app';

console.log(window.count); // undefined
console.log(window.name);  // "" — note: window.name is a pre-existing browser property, not your variable

Two important caveats:

  • ES modules don't leak at all. Top-level var and function declarations inside a <script type="module"> (or any import/export file) are module-scoped, so nothing — not even var — attaches to the global object.
  • Node.js files are modules too. A top-level var in a Node .js file is scoped to that module, so it does not attach to global the way a classic browser script would.

This difference is one of the practical reasons modern code prefers let/const and modules. For the full hoisting and scoping story behind var, read the old "var" and variable scope and closure.

Avoiding global pollution

Globals are shared, mutable state: any script can overwrite them, and name collisions cause bugs that are hard to trace. A few habits keep the namespace clean:

  • Prefer let/const and modules. They keep declarations out of the global object entirely.
  • Use a single namespace when you genuinely need a global, so you add one property instead of many.
  • Turn on strict mode. "use strict" makes an undeclared assignment like x = 5 throw instead of silently creating a global.
javascript— editable

ES modules keep the global namespace clean

ES modules split code into reusable files whose top-level declarations stay private to each file unless explicitly exported. This is the modern alternative to attaching things to the global object:

// file: math.js
export const add = (a, b) => a + b;

// file: app.js
import { add } from './math.js';
console.log(add(2, 3)); // 5
// `add` is imported, not read from a global — nothing leaks onto window/global.

Conclusion

The global object is the runtime's root scope: it hosts the language built-ins and host APIs, and in classic scripts it also collects top-level var and function declarations. Use globalThis to reach it portably, lean on let/const and modules to avoid polluting it, and enable strict mode to catch accidental globals early.

Practice

Practice
Which of the following are best practices when using the global object in JavaScript?
Which of the following are best practices when using the global object in JavaScript?
Was this page helpful?