The Global Object in JavaScript
Every JavaScript environment provides a special object that sits at the top of the scope chain and holds built-in functions, global variables, and environment-specific APIs. In browsers, it is window. In Node.js, it is global. In web workers, it is self. For years, writing cross-environment JavaScript meant guessing which name to use. Then globalThis arrived and unified everything.
This guide explains what the global object is, how it differs across environments, how variable declarations interact with it, what built-in properties it provides, and most importantly, how to use it responsibly without polluting the global namespace.
globalThis: The Universal Global Object
Before ES2020, accessing the global object in a cross-environment way was surprisingly awkward. You had to write something like this:
// The old, ugly way to get the global object
var globalObj =
typeof window !== "undefined" ? window :
typeof global !== "undefined" ? global :
typeof self !== "undefined" ? self :
this;
globalThis was introduced in ES2020 to solve this problem. It is a standardized reference to the global object that works in every JavaScript environment:
console.log(globalThis);
// In a browser: Window { ... }
// In Node.js: Object [global] { ... }
// In a worker: DedicatedWorkerGlobalScope { ... }
Using globalThis
globalThis always points to the global object, no matter where your code runs:
// Works everywhere
globalThis.myValue = 42;
console.log(globalThis.myValue); // 42
// Accessing built-in globals
console.log(globalThis.parseInt("10")); // 10
console.log(globalThis.isNaN(NaN)); // true
console.log(globalThis.Math.PI); // 3.141592653589793
console.log(globalThis.JSON.stringify({})); // "{}"
When to Use globalThis
In practice, you rarely need to reference globalThis directly. Most global functions like parseInt, setTimeout, and JSON are available without qualifying them:
// You don't need to write this:
globalThis.console.log("Hello");
globalThis.setTimeout(() => {}, 1000);
// Just write:
console.log("Hello");
setTimeout(() => {}, 1000);
globalThis is useful when you:
- Write code that must run in multiple environments (browser, Node.js, Deno, workers)
- Need to explicitly read or write a global property
- Create polyfills that need to attach to the global scope
- Check for the existence of environment-specific features
// Feature detection across environments
if (typeof globalThis.fetch === "function") {
console.log("Fetch API is available");
} else {
console.log("Fetch API is NOT available, need a polyfill");
}
// Adding a polyfill to the global scope
if (typeof globalThis.structuredClone === "undefined") {
globalThis.structuredClone = function(obj) {
return JSON.parse(JSON.stringify(obj));
};
}
window in Browsers, global in Node.js
Before globalThis, each JavaScript environment had its own name for the global object. Understanding these is important because you will encounter them in existing code.
window in Browsers
In browser environments, the global object is window. It represents the browser window or tab and contains everything from DOM APIs to timers to the JavaScript globals:
// In a browser console:
console.log(window === globalThis); // true
// window properties include:
console.log(window.innerWidth); // Browser window width
console.log(window.innerHeight); // Browser window height
console.log(window.location.href); // Current URL
console.log(window.document); // The DOM document
console.log(window.navigator.userAgent); // Browser info
window has a dual role in browsers:
- It is the global object (holds global variables and functions)
- It is the browser window object (has properties like
innerWidth,location,history)
// These are all equivalent in a browser:
console.log(window.setTimeout === setTimeout); // true
console.log(window.console === console); // true
console.log(window.Array === Array); // true
console.log(window.Math === Math); // true
console.log(window.globalThis === globalThis); // true
global in Node.js
In Node.js, the global object is called global:
// In Node.js:
console.log(global === globalThis); // true
// Node.js-specific globals
console.log(global.process.version); // e.g., "v20.10.0"
console.log(global.Buffer); // Buffer class for binary data
console.log(global.setTimeout); // Also available in Node.js
self in Web Workers and Service Workers
Web workers and service workers do not have a window object (because they have no DOM access). Their global object is referenced via self:
// Inside a Web Worker:
console.log(self === globalThis); // true
self.postMessage("Hello from the worker!");
Cross-Environment Summary
| Environment | Global Object Name | globalThis Works? |
|---|---|---|
| Browser (main thread) | window | Yes |
| Browser (web worker) | self | Yes |
| Browser (service worker) | self | Yes |
| Node.js | global | Yes (v12+) |
| Deno | globalThis (no window in scripts) | Yes |
When writing code that should work in multiple environments, always use globalThis. When writing browser-only code, using window is perfectly fine and is actually more common in practice. When writing Node.js-only code, you can use global, but globalThis is equally valid.
Global Variables and var vs. let/const at the Top Level
How a variable becomes "global" depends on how you declare it. The behavior is different for var, let/const, and undeclared assignments.
var at the Top Level Creates Global Object Properties
When you use var outside of any function, the variable becomes a property of the global object:
var appName = "MyApp";
console.log(window.appName); // "MyApp" (in a browser)
console.log(globalThis.appName); // "MyApp" (anywhere)
This means var declarations at the top level are accessible through the global object, which can lead to unintended interactions with other scripts on the page.
let and const Do NOT Create Global Object Properties
This is a crucial difference. let and const at the top level create global variables, but they do not become properties of the global object:
let userName = "Alice";
const API_KEY = "abc123";
console.log(window.userName); // undefined
console.log(window.API_KEY); // undefined
// But they ARE accessible as global variables in the script scope
console.log(userName); // "Alice"
console.log(API_KEY); // "abc123"
The variables exist in a "script scope" that is separate from the global object. They are globally accessible within your JavaScript code, but they do not pollute the window object.
Undeclared Variables (Implicit Globals)
Assigning a value to a variable without declaring it (no var, let, or const) creates a property on the global object in non-strict mode:
// Non-strict mode
function oops() {
accidentalGlobal = "I'm a global!"; // No declaration keyword!
}
oops();
console.log(window.accidentalGlobal); // "I'm a global!"
console.log(accidentalGlobal); // "I'm a global!"
This is one of the most dangerous pitfalls in JavaScript. A simple typo can create an unintended global variable:
function calculateTotal(items) {
let total = 0;
for (let i = 0; i < items.length; i++) {
toal += items[i].price; // Typo! "toal" instead of "total"
// In non-strict mode, this silently creates window.toal
}
return total; // Returns 0 because "total" was never modified
}
In strict mode, assigning to an undeclared variable throws a ReferenceError:
"use strict";
function safe() {
accidentalGlobal = "oops"; // ReferenceError: accidentalGlobal is not defined
}
Always use "use strict" or ES modules (which are strict by default) to prevent accidental global variable creation. Undeclared assignments are one of the most common sources of subtle bugs in JavaScript.
Explicit Global Object Assignment
When you intentionally want to create a global variable that is accessible across scripts or modules, assign it directly to the global object:
// Intentional global (clear and explicit)
globalThis.APP_CONFIG = {
apiUrl: "https://api.example.com",
debug: false,
version: "2.1.0"
};
// Accessible from anywhere
console.log(APP_CONFIG.apiUrl); // "https://api.example.com"
This is explicit and intentional, making it clear that the variable is meant to be global.
Complete Comparison
// 1. var (becomes a window property)
var varGlobal = "var";
console.log(window.varGlobal); // "var"
console.log(globalThis.varGlobal); // "var"
// 2. let (does NOT become a window property)
let letGlobal = "let";
console.log(window.letGlobal); // undefined
console.log(letGlobal); // "let" (accessible as a variable, just not on window)
// 3. const (does NOT become a window property)
const constGlobal = "const";
console.log(window.constGlobal); // undefined
console.log(constGlobal); // "const"
// 4. Undeclared (non-strict) (becomes a window property)
undeclaredGlobal = "oops";
console.log(window.undeclaredGlobal); // "oops"
// 5. Explicit assignment (becomes a window property)
globalThis.explicitGlobal = "explicit";
console.log(window.explicitGlobal); // "explicit"
| Declaration | Global variable? | Property of globalThis? |
|---|---|---|
var x = 1 (top level) | Yes | Yes |
let x = 1 (top level) | Yes | No |
const x = 1 (top level) | Yes | No |
x = 1 (undeclared, non-strict) | Yes | Yes |
globalThis.x = 1 | Yes | Yes |
Global Functions and Properties
The global object comes preloaded with a set of built-in functions, constructors, and values. These are available in all JavaScript environments.
Global Value Properties
console.log(globalThis.undefined); // undefined
console.log(globalThis.NaN); // NaN
console.log(globalThis.Infinity); // Infinity
You rarely access these through globalThis. They are typically used directly:
let x;
console.log(x === undefined); // true
console.log(isNaN(NaN)); // true
console.log(1 / 0 === Infinity); // true
Global Functions
These functions are available without importing or qualifying them:
// Parsing functions
console.log(parseInt("42px")); // 42
console.log(parseFloat("3.14abc")); // 3.14
// Encoding/Decoding URIs
console.log(encodeURIComponent("hello world & more"));
// "hello%20world%20%26%20more"
console.log(decodeURIComponent("hello%20world"));
// "hello world"
console.log(encodeURI("https://example.com/path with spaces"));
// "https://example.com/path%20with%20spaces"
// Type checking
console.log(isNaN("abc")); // true
console.log(isFinite(42)); // true
console.log(isFinite(Infinity)); // false
Global Constructors and Objects
The global object holds all built-in constructors:
// Primitive wrappers
console.log(globalThis.String); // ƒ String()
console.log(globalThis.Number); // ƒ Number()
console.log(globalThis.Boolean); // ƒ Boolean()
console.log(globalThis.Symbol); // ƒ Symbol()
console.log(globalThis.BigInt); // ƒ BigInt()
// Data structures
console.log(globalThis.Object); // ƒ Object()
console.log(globalThis.Array); // ƒ Array()
console.log(globalThis.Map); // ƒ Map()
console.log(globalThis.Set); // ƒ Set()
console.log(globalThis.WeakMap); // ƒ WeakMap()
console.log(globalThis.WeakSet); // ƒ WeakSet()
// Error types
console.log(globalThis.Error); // ƒ Error()
console.log(globalThis.TypeError); // ƒ TypeError()
console.log(globalThis.RangeError); // ƒ RangeError()
// Other built-ins
console.log(globalThis.Promise); // ƒ Promise()
console.log(globalThis.RegExp); // ƒ RegExp()
console.log(globalThis.Date); // ƒ Date()
console.log(globalThis.ArrayBuffer); // ƒ ArrayBuffer()
// Utility objects (not constructors)
console.log(globalThis.Math); // Math object
console.log(globalThis.JSON); // JSON object
console.log(globalThis.console); // Console object
console.log(globalThis.Intl); // Intl object
Browser-Specific Globals
In browsers, window provides additional properties that are not part of the JavaScript language but are part of the Web APIs:
// Timers
setTimeout(() => console.log("delayed"), 1000);
setInterval(() => console.log("repeated"), 2000);
requestAnimationFrame(() => console.log("before repaint"));
// Network
fetch("https://api.example.com/data");
// DOM
document.getElementById("app");
document.querySelector(".container");
// Navigation
console.log(location.href);
console.log(navigator.language);
console.log(history.length);
// Storage
localStorage.setItem("key", "value");
sessionStorage.getItem("key");
// Dialogs
alert("Hello!");
const answer = confirm("Are you sure?");
const input = prompt("Enter your name:");
// Screen info
console.log(screen.width);
console.log(screen.height);
Node.js-Specific Globals
Node.js provides its own set of globals:
// Process information
console.log(process.env.NODE_ENV);
console.log(process.argv);
console.log(process.cwd());
console.log(process.pid);
// Buffer for binary data
const buf = Buffer.from("Hello");
// Timers (also available, same as browser)
setTimeout(() => {}, 1000);
setInterval(() => {}, 1000);
setImmediate(() => {}); // Node.js-specific
// Module system (CommonJS, in non-ESM files)
// __dirname, __filename, require, module, exports
// Note: these are NOT truly global (they are module-scoped)
In Node.js, __dirname, __filename, require, module, and exports look like globals, but they are actually parameters injected into each module by the Node.js module wrapper. They are scoped to each file, not truly global.
Using the Global Object Responsibly (Avoid Polluting)
"Global namespace pollution" is the practice of adding too many variables and functions to the global scope, increasing the risk of naming collisions and making code harder to maintain.
The Problem with Global Pollution
Consider a web page that loads three scripts:
<script src="analytics.js"></script>
<script src="utils.js"></script>
<script src="app.js"></script>
If all three scripts use var to declare top-level variables:
// analytics.js
var data = fetchAnalyticsData();
var config = { trackClicks: true };
// utils.js
var config = { dateFormat: "YYYY-MM-DD" }; // Overwrites analytics config!
var data = []; // Overwrites analytics data!
// app.js
console.log(config); // { dateFormat: "YYYY-MM-DD" } (not what analytics expected!)
All three scripts share the same global scope. Variables from one script can silently overwrite variables from another. This was a massive problem in the pre-module era of JavaScript.
Rule 1: Use Modules
The best way to avoid global pollution is to use ES modules. Each module has its own scope, and nothing leaks to the global object unless explicitly exported:
<script type="module" src="analytics.js"></script>
<script type="module" src="utils.js"></script>
<script type="module" src="app.js"></script>
// analytics.js (module)
const data = fetchAnalyticsData(); // Scoped to this module
const config = { trackClicks: true }; // Not visible outside
export { data, config };
// utils.js (module)
const config = { dateFormat: "YYYY-MM-DD" }; // No conflict!
export { config };
// app.js (module)
import { config as analyticsConfig } from "./analytics.js";
import { config as utilsConfig } from "./utils.js";
// No conflicts (different names)
Rule 2: Use let and const, Never var at the Top Level
If you must write non-module scripts, use let and const instead of var. They do not create properties on the global object:
// This does NOT pollute window
let appState = { initialized: false };
const APP_VERSION = "2.0";
// Verify:
console.log(window.appState); // undefined
console.log(window.APP_VERSION); // undefined
Rule 3: Namespace Pattern (Pre-Module Legacy)
Before modules, the standard practice was to use a single global object as a namespace:
// Instead of many globals:
// var users = [];
// var config = {};
// function getUser() {}
// function setConfig() {}
// Use one global namespace:
var MyApp = MyApp || {};
MyApp.users = [];
MyApp.config = {};
MyApp.getUser = function(id) { /* ... */ };
MyApp.setConfig = function(key, value) { /* ... */ };
// Only "MyApp" is added to the global scope
You can nest namespaces for larger applications:
var MyApp = MyApp || {};
MyApp.Models = MyApp.Models || {};
MyApp.Views = MyApp.Views || {};
MyApp.Utils = MyApp.Utils || {};
MyApp.Models.User = function(name) {
this.name = name;
};
MyApp.Utils.formatDate = function(date) {
return date.toISOString();
};
Rule 4: Minimal Intentional Globals
Sometimes you genuinely need a global variable, perhaps for application-wide configuration, feature flags, or a library's public API. When you do, be explicit about it:
// GOOD: Explicit and intentional
globalThis.APP_CONFIG = Object.freeze({
apiUrl: "https://api.example.com",
environment: "production",
features: {
darkMode: true,
betaAccess: false
}
});
// Object.freeze prevents accidental modification
// APP_CONFIG.apiUrl = "hacked"; // Silently fails (or throws in strict mode)
Rule 5: Check Before Adding to Global Scope
When creating polyfills or library globals, always check if the name is already taken:
// Polyfill: only add if not already present
if (typeof globalThis.structuredClone !== "function") {
globalThis.structuredClone = function(value) {
return JSON.parse(JSON.stringify(value));
};
}
// Library: warn if name is already taken
if (typeof globalThis.MyLibrary !== "undefined") {
console.warn("MyLibrary is already defined! Possible conflict.");
}
globalThis.MyLibrary = { /* ... */ };
What NOT to Do
// DON'T: Create many top-level var variables
var userData = {};
var isLoggedIn = false;
var currentPage = "home";
var apiToken = "secret";
// All of these become window properties and can conflict
// DON'T: Forget to declare variables
function processItems(items) {
for (i = 0; i < items.length; i++) { // "i" leaks to global!
result = items[i] * 2; // "result" leaks to global!
}
}
// DON'T: Use window/global as a convenience storage
window.tempData = someComputation(); // Use a local variable or module instead
Checking for Global Pollution
You can check how many globals your code has added by comparing with a clean browser window:
// Run this in the console to find custom globals
(function() {
const iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
const cleanGlobals = Object.keys(iframe.contentWindow);
const currentGlobals = Object.keys(window);
const added = currentGlobals.filter(key => !cleanGlobals.includes(key));
console.log("Custom globals:", added);
document.body.removeChild(iframe);
})();
Summary
| Concept | Key Takeaway |
|---|---|
globalThis | The standardized, cross-environment reference to the global object (ES2020) |
window | The global object in browsers; also represents the browser window |
global | The global object in Node.js |
var at top level | Creates a property on the global object |
let/const at top level | Does NOT create a property on the global object |
| Undeclared assignment | Creates a global property in non-strict mode (dangerous!) |
| Built-in globals | Math, JSON, console, parseInt, setTimeout, constructors, etc. |
| Global pollution | Adding too many names to the global scope, risking collisions |
| Best practice | Use ES modules, let/const, and strict mode; keep globals to an absolute minimum |
The global object is a fundamental part of JavaScript, but in modern code, you should interact with it as little as possible. Use modules for code organization, let and const for variables, and globalThis only when you genuinely need cross-environment access to the global scope. The less your code depends on global state, the more predictable, testable, and maintainable it will be.