The global Object
In a browser, the top-level object is window. In Node.js it is global. It holds the values and functions available everywhere without an import. Knowing exactly what lives on global — and, just as importantly, what only looks global but is actually injected per-file — prevents a surprising amount of confusion later, especially when you move between CommonJS and ES modules.
global vs globalThis
globalThis is the standard, cross-platform reference to the global object, introduced in ES2020. It equals global in Node and window in browsers, so prefer it in any code meant to run in both places.
console.log(global === globalThis) // true in Node // Anything assigned here is visible everywhere in the process: globalThis.appName = 'my-service' console.log(appName) // 'my-service' (no import needed)
true my-service
What actually lives on global
Global | Category | Purpose |
|---|---|---|
| Diagnostics | Logging to stdout/stderr |
| Runtime | Process info & control |
| Binary | Node's binary data type |
| Timers | Schedule code later / repeatedly |
| Timers | Run after the current poll phase |
| Scheduling | Schedule a microtask |
| Web standard | HTTP requests |
| Web standard | Parse and build URLs |
| Web standard | Text ⇄ bytes |
| Web standard | Deep-clone objects |
| Web standard | Cancel async operations |
| Web standard | Web Crypto ( |
Notice how many are Web Platform APIs — Node has deliberately adopted them so code is portable with the browser. See Node.js vs Browser JavaScript.
Module-scoped "globals" that are not really global
These feel global but are actually per-module values injected by the CommonJS module wrapper. Each file gets its own — __dirname differs from file to file — so they are not properties of global:
CommonJS only
console.log(__dirname) // directory of THIS file
console.log(__filename) // absolute path of THIS file
console.log(module) // this module's metadata object
console.log(exports) // shorthand alias for module.exports
require('./other') // import another module
console.log(global.__dirname) // undefined — proof it is not globalESM equivalents
import { fileURLToPath } from 'node:url'
import { dirname } from 'node:path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)Inspecting the global object
// List everything currently on the global object: console.log(Object.getOwnPropertyNames(globalThis)) // [ 'global', 'queueMicrotask', 'clearImmediate', 'setImmediate', // 'structuredClone', 'fetch', 'crypto', 'process', 'Buffer', ... ]
Why polluting global is a bad idea
It is tempting to stash shared state on global (a config object, a database connection). Resist it:
Hidden coupling — any file can mutate it, so behaviour becomes hard to trace.
Name collisions — two libraries (or your code and a dependency) may fight over the same key.
Testing pain — global state leaks between tests unless carefully reset.
No type safety — TypeScript cannot easily track ad-hoc global properties.
Prefer modules for sharing — export and import explicit values. A module is evaluated once and its exports are cached, so a module-level singleton gives you shared state without touching global:
The module-singleton pattern (preferred)
// db.js — evaluated once; every importer gets the same instance
import { Pool } from 'pg'
export const pool = new Pool({ connectionString: process.env.DATABASE_URL })
// users.js
import { pool } from './db.js' // same shared pool, no global needed