typeof global and typeof globalThis in Node.js — A Deep Dive

If you have spent any time writing JavaScript across different environments — a browser, Node.js, a Web Worker — you have probably run into confusing situations around the "global object." What is it? Why does it have different names in different places? Why does typeof global work in Node but not in the browser? And what problem does globalThis actually solve?
This post answers all of those questions with thorough examples you can run yourself.
First: What is the typeof Operator?
Before diving into global and globalThis, let's make sure typeof is completely clear.
typeof is a unary operator that returns a string describing the type of its operand. It never throws an error — even on variables that have never been declared.
// All the possible return values of typeof
console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof Symbol()); // "symbol"
console.log(typeof 42n); // "bigint"
console.log(typeof {}); // "object"
console.log(typeof []); // "object" (arrays are objects)
console.log(typeof null); // "object" (famous historical bug in JS)
console.log(typeof function(){}); // "function"
The most important property of typeof for our discussion is that it is safe to use on undeclared variables. Accessing an undeclared variable directly throws a ReferenceError, but typeof quietly returns "undefined".
// Direct access to an undeclared variable — throws
try {
console.log(undeclaredVariable);
} catch (e) {
console.log(e.message); // "undeclaredVariable is not defined"
}
// typeof on the same undeclared variable — safe, no throw
console.log(typeof undeclaredVariable); // "undefined"
This safety behavior is the reason typeof is commonly used for feature detection, which we will come back to later.
The Global Object — Why Does It Even Exist?
In JavaScript, the global object is a special object that:
Holds all built-in globals (
setTimeout,console,Math,JSON, etc.)Holds any variables you declare at the top level of a script (in browsers, but not in Node modules — more on this below)
Is the value of
thisin non-strict mode at the top level
The problem is that this object has different names depending on where your code runs:
| Environment | Global object name |
|---|---|
| Browser | window |
| Node.js | global |
| Web Worker | self |
| Any (ES2020+) | globalThis |
globalThis was introduced precisely to fix this inconsistency.
typeof global in Node.js
global is the Node.js-specific name for the global object. It has existed since Node.js was first released.
// In Node.js
console.log(typeof global); // "object"
console.log(global); // Object [global] { ... }
What lives on global?
global holds everything that is available without importing anything. You can inspect it directly:
// A sample of what you find on global in Node.js
console.log(typeof global.setTimeout); // "function"
console.log(typeof global.clearTimeout); // "function"
console.log(typeof global.setInterval); // "function"
console.log(typeof global.console); // "object"
console.log(typeof global.process); // "object"
console.log(typeof global.Buffer); // "function"
console.log(typeof global.queueMicrotask); // "function"
console.log(typeof global.structuredClone);// "function"
console.log(typeof global.__filename); // "undefined" (that's module-scoped, not global)
console.log(typeof global.__dirname); // "undefined" (same)
Variables you assign to global become globally accessible
// Assigning a property directly to global
global.appConfig = {
version: "1.0.0",
debug: false,
};
// Now accessible anywhere in the same process without importing
console.log(appConfig.version); // "1.0.0"
console.log(appConfig.debug); // false
// And it goes the other way too
global.greeting = "Hello from global";
console.log(greeting); // "Hello from global"
This is generally considered bad practice in production code, but it is useful to understand how it works.
global does not exist in browsers
If you copy Node.js code that references global into a browser, it will throw a ReferenceError. This is one of the reasons globalThis was invented.
// In a browser:
console.log(typeof global); // "undefined" — not defined in browsers
// console.log(global); // ReferenceError: global is not defined
typeof globalThis in Node.js
globalThis was introduced in ES2020 and is supported in Node.js 12 and above. It is the standardized, universal way to refer to the global object regardless of environment.
// In Node.js
console.log(typeof globalThis); // "object"
global and globalThis are the same object in Node.js
They are not just equivalent — they are literally the same reference.
console.log(global === globalThis); // true
// Setting a property via global is visible on globalThis and vice versa
global.myVar = "set via global";
console.log(globalThis.myVar); // "set via global"
globalThis.myOtherVar = "set via globalThis";
console.log(global.myOtherVar); // "set via globalThis"
globalThis also holds all Node.js built-ins
console.log(globalThis.setTimeout === setTimeout); // true
console.log(globalThis.console === console); // true
console.log(globalThis.process === process); // true
console.log(globalThis.Buffer === Buffer); // true
console.log(globalThis.queueMicrotask === queueMicrotask); // true
Every built-in you use in Node.js without importing lives on globalThis.
The Critical Difference: global vs globalThis in Practice
The real-world difference shows up when you write code that needs to run in both Node.js and the browser.
// This works in Node.js but throws ReferenceError in a browser
console.log(global.process); // works in Node
// console.log(global.window); // undefined in Node, works in browser as window.window
// This works everywhere — Node, browser, Web Worker
console.log(globalThis.process); // process object in Node, undefined in browser
console.log(globalThis.window); // undefined in Node, Window object in browser
globalThis is the safe choice for any code meant to run in multiple environments.
Node.js Module Scope vs Global Scope
This is one of the most misunderstood aspects of Node.js. In a browser, a var declared at the top level of a script becomes a property of window. In Node.js, that is not the case.
Every Node.js file is silently wrapped in a module function by the runtime, roughly like this:
// Node.js internally wraps every file you write in something like this:
(function(exports, require, module, __filename, __dirname) {
// Your entire file's code goes here
var myVar = "hello"; // This is local to the wrapper function, not global
});
This means top-level var, let, and const in a Node module do not end up on global.
// In a Node.js .js file (not the -e flag):
var topLevelVar = "I am top-level var";
let topLevelLet = "I am top-level let";
const topLevelConst = "I am top-level const";
console.log(global.topLevelVar); // undefined — module wrapper keeps it local
console.log(global.topLevelLet); // undefined
console.log(global.topLevelConst); // undefined
// To make something truly global, you must assign explicitly:
global.trulyGlobal = "now I am global";
console.log(globalThis.trulyGlobal); // "now I am global"
This is a major difference from browser behavior, where var at the top level becomes window.varName.
typeof for Safe Feature Detection
Because typeof never throws, it is the standard tool for checking whether something exists on the global object before using it.
Detecting which environment you are in
function detectEnvironment() {
if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node) {
return "Node.js v" + globalThis.process.versions.node;
}
if (typeof globalThis.window !== "undefined") {
return "Browser";
}
if (typeof globalThis.self !== "undefined" && typeof globalThis.window === "undefined") {
return "Web Worker";
}
return "Unknown environment";
}
console.log(detectEnvironment()); // "Node.js v22.22.0" (or whatever version you have)
Checking for specific APIs before using them
// Safe check for fetch — available natively in Node 18+, all modern browsers
if (typeof globalThis.fetch !== "undefined") {
console.log("fetch is available, using it...");
// globalThis.fetch("https://api.example.com/data");
} else {
console.log("fetch is not available, need a polyfill or node-fetch");
}
// Safe check for localStorage — only exists in browsers
if (typeof globalThis.localStorage !== "undefined") {
console.log("Running in a browser with localStorage");
} else {
console.log("No localStorage — probably Node.js");
}
// Safe check for Buffer — Node.js only
if (typeof globalThis.Buffer !== "undefined") {
const buf = Buffer.from("hello", "utf8");
console.log("Buffer available:", buf.toString("hex")); // "68656c6c6f"
} else {
console.log("Buffer not available — running in browser");
}
// Safe check for process — Node.js only
if (typeof globalThis.process !== "undefined") {
console.log("Node.js process object found");
console.log("Platform:", process.platform); // "linux", "darwin", "win32", etc.
console.log("Node version:", process.version);
}
Writing a universal global getter — the old way vs the new way
Before globalThis existed, library authors had to write awkward code to find the global object:
// The old pattern — seen in many older libraries
function getGlobalObjectOldWay() {
if (typeof globalThis !== "undefined") return globalThis; // modern
if (typeof global !== "undefined") return global; // Node.js
if (typeof window !== "undefined") return window; // browser
if (typeof self !== "undefined") return self; // Web Worker
throw new Error("Cannot determine global object");
}
const g = getGlobalObjectOldWay();
console.log(typeof g); // "object"
// The new way — just use globalThis directly
// It is available in all environments that matter (Node 12+, all modern browsers)
console.log(typeof globalThis); // "object" — always works
Practical Patterns Using globalThis
1. Writing a cross-environment polyfill
// Polyfill for a missing API — works in both Node and browser
if (typeof globalThis.myCustomLogger === "undefined") {
globalThis.myCustomLogger = function (message) {
const timestamp = new Date().toISOString();
console.log(`[\({timestamp}] \){message}`);
};
}
// Now usable anywhere in the codebase without importing
myCustomLogger("Application started"); // [2025-03-13T...] Application started
2. Sharing state across modules (use carefully)
// moduleA.js
globalThis.__appState = globalThis.__appState || {
requestCount: 0,
startTime: Date.now(),
};
globalThis.__appState.requestCount++;
console.log("Requests so far:", globalThis.__appState.requestCount);
// moduleB.js — accessing the same state without any import
console.log("From module B:", globalThis.__appState.requestCount);
3. Environment-aware config loader
function loadConfig() {
const isNode =
typeof globalThis.process !== "undefined" &&
typeof globalThis.process.versions?.node !== "undefined";
const isBrowser = typeof globalThis.window !== "undefined";
if (isNode) {
return {
environment: "node",
storagePath: globalThis.process.env.STORAGE_PATH || "/tmp/data",
logLevel: globalThis.process.env.LOG_LEVEL || "info",
platform: globalThis.process.platform,
};
}
if (isBrowser) {
return {
environment: "browser",
storagePath: "localStorage",
logLevel: "warn",
platform: globalThis.navigator?.platform || "unknown",
};
}
return { environment: "unknown" };
}
const config = loadConfig();
console.log(config);
// {
// environment: 'node',
// storagePath: '/tmp/data',
// logLevel: 'info',
// platform: 'linux'
// }
4. Type-safe global variable access
// Instead of directly accessing a global and hoping it exists:
// console.log(someGlobal.value); // might throw ReferenceError
// Use typeof to guard it first
function safeGetGlobal(key) {
if (typeof globalThis[key] !== "undefined") {
return globalThis[key];
}
return null;
}
globalThis.userSession = { userId: 42, role: "admin" };
const session = safeGetGlobal("userSession");
const missing = safeGetGlobal("somethingThatDoesntExist");
console.log(session); // { userId: 42, role: 'admin' }
console.log(missing); // null
Summary: Key Differences at a Glance
global |
globalThis |
|
|---|---|---|
| Available in Node.js | Yes | Yes (Node 12+) |
| Available in browsers | No | Yes |
| Available in Web Workers | No | Yes |
| Same object in Node.js | — | global === globalThis is true |
| Standardized (ES spec) | No (Node-specific) | Yes (ES2020) |
| Safe to use in shared code | No | Yes |
Quick Recap
typeofalways returns a string and never throws, even on undeclared variables.globalis Node.js's name for the global object. It does not exist in browsers.globalThisis the standardized ES2020 name that works in every environment.In Node.js,
global === globalThisistrue— they are the same object.Top-level
varin a Node.js module does not go onglobalthe way it would in a browser script, because Node wraps every file in a module function.Use
typeof globalThis.something !== "undefined"for safe, cross-environment feature detection.Prefer
globalThisoverglobalin any new code, especially code that might run in multiple environments.



