skip to content

What is globalThis in JavaScript, and what problem does it solve that window, self, and global did not?

level: juniorimportance: should knowfreq 55%

answer

  1. one name, every environment
  2. window, self, global all differ
  3. added by a recent language edition
  4. the old trick built a function from a string

basics

~20 s

globalThis is the single standard name for the global object in every JavaScript environment. Browsers expose window, workers expose self, Node exposes global; globalThis, added in ES2020, resolves to the right one everywhere without feature sniffing.

solid answer

~40 s

Before ES2020 there was no portable way to name the global object: a browser page has `window`, a Web Worker has `self`, Node has `global`, and none of those names exists in all three. Library code ended up with sniffing chains like `typeof window !== 'undefined' ? window : global`, or the `Function('return this')()` trick, which fails wherever building code from strings is disallowed. `globalThis` is a property of the global object whose value is the global object itself, so the same identifier works in pages, workers and Node. It is a portable *name*, not a portable *API surface*: `globalThis` exists everywhere, but `globalThis.document` still only exists where a document does. It is also writable and configurable, so it can be shadowed by a local binding.

code

javascript · 11 lines
javascript
// Runs as a classic browser script (not type="module")
var a = 1;
let b = 2;
function c() {}

console.log(globalThis.a); // 1
console.log(globalThis.b); // undefined
console.log(typeof globalThis.c); // "function"

globalThis.d = 4; // the explicit, portable way
console.log(d); // 4

go deeper

for a junior

Know that globalThis names the global object in browsers, workers and Node alike, and say plainly that window and global are environment-specific names for the same kind of thing.

for a middle

Be ready to explain why the old sniffing chains and the Function('return this')() trick were fragile, and which top-level declarations in a classic script actually appear as properties of the global object.

for a senior

Show judgment about globals themselves: use globalThis for polyfill installation and capability detection, keep application state out of it, and make every global write in a module an explicit, greppable assignment.

for a principal

Own the policy question — where cross-environment code is allowed to touch the global object at all, how shared bundles avoid colliding on global names, and why capability detection beats environment detection in a codebase shipping to multiple runtimes.

## The problem globalThis solves JavaScript has always had a global object — the object that holds the global bindings and the built-ins — but for most of its history it had no standard *name*. Each host invented its own: - a browser page exposes it as `window` (and, historically, `frames` and `self`); - a Web Worker exposes it as `self`, and has no `window` at all; - Node exposes it as `global`, and has neither `window` nor `self`. So any code meant to run in more than one of those places could not simply write the global object's name. That is a real problem for polyfills, feature detection, and any library that wants to stash or read something globally. ## The workarounds, and why each one broke The sniffing chain was the common approach: ```js var root = typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : typeof global !== 'undefined' ? global : undefined; ``` It works but has to be extended for every new host, and it silently produces `undefined` in one it does not know about. The clever alternative was `Function('return this')()`. A function created from a string is not in strict mode by default, and a non-strict function called with no receiver gets the global object as `this`. That is portable in principle, but it constructs code from a string, which is exactly what environments that forbid dynamic code evaluation block — so the trick throws in precisely the hardened environments where you least want a surprise. A third habit was `var root = this;` at the top of a file. That works only in a classic script; in an ES module top-level `this` is `undefined`, and in a CommonJS module it is `module.exports`. ## What globalThis actually is ES2020 added `globalThis` as a property of the global object whose value is the global object. Its attributes are writable, configurable, and non-enumerable — it is an ordinary data property, not magic syntax. That means all of these hold in their respective hosts: ```js // browser page globalThis === window; // true // Web Worker globalThis === self; // true // Node globalThis === global; // true ``` Because it is a normal property and a normal identifier, a local binding named `globalThis` shadows it inside that scope, and `globalThis` itself can be reassigned. In practice nobody does either, but it explains why very defensive library code still guards. ## Portable name, not portable capabilities The most common misreading is that `globalThis` unifies the *environments*. It does not — it unifies the *name*. Whatever the host does or does not provide is unchanged: reading `globalThis.document` in a non-browser environment gives `undefined` rather than a document, and the correct check is still a capability check (`typeof globalThis.document !== 'undefined'`) rather than an environment check. ## Which declarations end up on globalThis A related detail that interviewers like: in a **classic script**, top-level `var` declarations and function declarations create properties on the global object, while `let`, `const` and `class` create bindings in a separate global declarative scope that is *not* reflected on the global object. ```js // classic script var a = 1; let b = 2; globalThis.a; // 1 globalThis.b; // undefined ``` In an **ES module** none of them touch the global object — module top-level scope is module-local. So the only deliberate way to publish something globally from a module is an explicit assignment: `globalThis.myThing = myThing`. Being explicit is a feature, not a limitation: it makes global writes greppable instead of an accident of declaration keyword. ## When you should reach for it Use `globalThis` when you genuinely need the global object: installing or detecting a polyfill, reading a global injected by another script, or writing environment-agnostic library code. Do not use it as a general-purpose place to hang application state — a global is a global, with all the coupling and testing pain that implies, no matter how portable its name is.

  • If globalThis exists everywhere, why do libraries still feature-detect instead of just checking which globals exist?
    Because `globalThis` unifies the name, not the capabilities. A bundle running in Node still has no `document`, and one running in a worker still has no `window`. The robust pattern is to reach the global portably and then test for the specific API you need — `typeof globalThis.fetch === 'function'` — rather than inferring an entire environment from one marker global.
  • Can globalThis be shadowed or reassigned, and does that matter?
    Yes. It is an ordinary writable, configurable, non-enumerable property of the global object, so `globalThis = something` at the top level replaces it, and a local `let globalThis` shadows it inside that scope. It matters mainly for library authors who capture the value once at module initialisation rather than re-reading the identifier on every use.
  • How would you publish a value globally from an ES module?
    With an explicit assignment: `globalThis.APP_CONFIG = config`. Module top-level `var`, `let`, `const`, `function` and `class` declarations are all module-scoped and never appear on the global object, unlike top-level `var` and function declarations in a classic script. The explicitness is an advantage — every global write is a searchable statement rather than a side effect of the declaration keyword.

saying these in an interview costs you the question

  • Says globalThis is just another name for window in every environment
  • Claims globalThis gives Node access to DOM APIs
  • Thinks top-level this in a module equals globalThis
  • Believes let at script top level creates a globalThis property
  • Says globalThis is a keyword that cannot be shadowed

context