Contents
  1. How Global Variables Are Created
  2. The Problem: Namespace Collision
  3. The Problem: Unpredictable State
  4. The Problem: var and Function Scope
  5. The Fix: let, const and Strict Mode
  6. The Fix: Single Global with Namespacing
  7. What to Do Now
← All posts

Awful Parts in JS: Global Variables and Scope

Douglas Crockford called it the worst of JavaScript's bad features: its dependence on global variables. Here is why, and what to do about it.

Douglas Crockford, in JavaScript: The Good Parts, states directly: the worst of all of JavaScript’s bad features is its dependence on global variables. A global variable is a variable visible in every scope. While other languages have global variables, JavaScript does not merely allow them, it structurally requires them in ways that other languages do not.

This blog covers what makes global variables problematic, how JavaScript creates them accidentally, and what to do about it.

How Global Variables Are Created

There are three ways a global variable comes into existence in JavaScript.

The first is an explicit declaration outside any function:

var name = 'Alice';

The second is by adding a property directly to the global object. In a browser, the global object is window:

window.name = 'Alice';

The third, and most dangerous, is implicit creation. If a variable is used inside a function without being declared with var, let, or const, JavaScript creates it as a global variable automatically.

function setName() {
  name = 'Alice'; // no var, let, or const
}

setName();
console.log(name); // 'Alice' - now a global variable

This behaviour is a silent error. No warning is produced. The variable leaks into the global scope, where it can interfere with anything else in the program.

The Problem: Namespace Collision

All JavaScript loaded on a page shares the same global scope. If two scripts declare a global variable with the same name, the second declaration overwrites the first. JavaScript does not raise an error when this happens.

// Script A
var total = 100;

// Script B, loaded after
var total = 0;

console.log(total); // 0 - Script A's value is silently gone

In a large application with multiple files and third-party libraries, this produces bugs that are difficult to diagnose because the overwrite happens silently at load time.

The Problem: Unpredictable State

Because a global variable can be read and modified from any part of the program at any time, the state it holds cannot be trusted at any given point. A function that reads a global variable may receive a different value depending on what has run before it, making the function’s output non-deterministic and difficult to test.

The Problem: var and Function Scope

The var keyword is function-scoped. It does not respect block boundaries. Variables declared with var inside if blocks or for loops leak into the containing function or global scope:

for (var i = 0; i < 3; i++) {
  // i is not block-scoped
}

console.log(i); // 3 - leaked out of the loop

This was the only variable declaration keyword available before ES6. Codebases written before 2015 carry this behaviour throughout.

The Fix: let, const and Strict Mode

let and const, introduced in ES6, are block-scoped. They do not leak outside the block they are declared in, and they cannot be implicitly created without a declaration.

for (let i = 0; i < 3; i++) {
  // i is block-scoped
}

console.log(i); // ReferenceError: i is not defined

Strict mode prevents implicit global variable creation. Adding 'use strict' at the top of a file or function causes an undeclared assignment to throw a ReferenceError instead of silently creating a global.

'use strict';

function setName() {
  name = 'Alice'; // ReferenceError: name is not defined
}

The Fix: Single Global with Namespacing

For code that predates ES6 modules, Crockford recommends minimising global use by creating a single global variable as a container for the entire application:

var MYAPP = {};

MYAPP.user = { name: 'Alice' };
MYAPP.config = { debug: false };

One global variable instead of many. All application state is nested under it. If another library also creates a global, the risk of collision is reduced to a single name rather than every variable the application uses.

What to Do Now

Add 'use strict' to the top of a JavaScript file and attempt to use a variable without declaring it:

'use strict';

undeclaredVariable = 'test'; // ReferenceError

The error surfaces immediately rather than silently creating a global. Prefer const and let for all new code, and treat any use of var in a codebase as a location worth reviewing for scope-related bugs.

← All posts