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 exposeself, plusframes). - Web Workers call it
self(there is nowindowin 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.jsTrying to use the wrong name throws a ReferenceError, which is exactly the problem globalThis solves:
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:
- 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 likeparseInt,isNaN, andeval. - Host (environment) APIs, added by the runtime:
document,fetch,localStorage,setTimeout, andalertin the browser;process,Buffer, andrequirein 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 variableTwo important caveats:
- ES modules don't leak at all. Top-level
varandfunctiondeclarations inside a<script type="module">(or anyimport/exportfile) are module-scoped, so nothing — not evenvar— attaches to the global object. - Node.js files are modules too. A top-level
varin a Node.jsfile is scoped to that module, so it does not attach toglobalthe 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/constand 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 likex = 5throw instead of silently creating a global.
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.