Table of Contents#
- Understanding the Global Object
- var in Global Scope: Creating Global Object Properties
- let in Global Scope: No Global Object Properties
- Why the Difference? The Global Lexical Environment
- const and class Declarations: Same Behavior as let
- Practical Implications and Common Pitfalls
- Conclusion
- References
1. Understanding the Global Object#
Before we compare var and let, let’s clarify what the "global object" is. The global object is a special object in JavaScript that exists in the global scope. It serves as a container for global variables and functions, and its name depends on the environment:
- Browsers: The global object is
window(orselfin web workers,framesin framesets). - Node.js: The global object is
global. - Other environments: e.g.,
thisin some non-browser contexts (thoughthisrefers to the global object only in the global scope).
Variables declared in the global scope can sometimes become properties of this global object, but not always—it depends on how they’re declared.
2. var in Global Scope: Creating Global Object Properties#
Before ES6, var was the only way to declare variables (other than implicit global variables, which are discouraged). A key trait of var is that variables declared with var in the global scope become properties of the global object.
Example: var Attaches to the Global Object#
// Declare a variable with var in global scope
var globalVar = "I'm a var variable";
// Access it as a property of the global object (window in browsers)
console.log(window.globalVar); // Output: "I'm a var variable"Here, globalVar is not just a global variable—it’s also a property of window. This means you can access it via window.globalVar or even this.globalVar (since this in the global scope refers to the global object).
Why Does var Do This?#
Historically, JavaScript’s global scope was tightly coupled to the global object. var was designed to work with this model: global var declarations directly extend the global object’s properties. This behavior made var variables mutable and enumerable as global object properties (you could even delete them with delete, though this is not recommended).
3. let in Global Scope: No Global Object Properties#
ES6 introduced let (and const) to address scoping issues with var (e.g., hoisting quirks and lack of block scoping). A critical difference is that let declarations in the global scope do NOT create properties on the global object.
Example: let Does Not Attach to the Global Object#
// Declare a variable with let in global scope
let globalLet = "I'm a let variable";
// Try to access it as a window property: undefined
console.log(window.globalLet); // Output: undefinedEven though globalLet is a global variable (accessible anywhere in the global scope), it is not a property of window. This is a deliberate design choice in ES6.
Proof: let Variables Are Not Enumerable on the Global Object#
We can further confirm this by checking if globalLet exists in the global object’s properties using Object.hasOwn (or Object.prototype.hasOwnProperty):
let globalLet = "test";
console.log(Object.hasOwn(window, "globalLet")); // Output: false (not a property)
console.log(Object.hasOwn(window, "globalVar")); // Output: true (var is a property)4. Why the Difference? The Global Lexical Environment#
To understand why let and var behave differently, we need to explore how JavaScript manages the global scope. ES6 introduced the concept of the global lexical environment, which coexists with the global object but operates independently.
The Global Environment Record#
The global scope is governed by a "global environment record," which has two components:
-
Object Environment Record: Manages bindings (variables/functions) that are properties of the global object. This includes:
- Variables declared with
var. - Function declarations (e.g.,
function myFunc() {}).
- Variables declared with
-
Declarative Environment Record: Manages bindings that are NOT properties of the global object. This includes:
- Variables declared with
letandconst. classdeclarations.
- Variables declared with
In other words:
varuses the object environment record, attaching to the global object.letuses the declarative environment record, existing only in the lexical scope (not the global object).
Visualizing the Global Scope#
Think of the global scope as having two "buckets":
- One bucket is the global object (for
var, function declarations). - The other bucket is the declarative environment (for
let,const,class).
Variables in the declarative bucket are still global (accessible everywhere in the global scope) but are not tied to the global object.
5. const and class Declarations: Same Behavior as let#
The behavior of let in the global scope extends to const and class declarations. They too are stored in the declarative environment record and do not become properties of the global object.
Example: const in Global Scope#
const globalConst = 42;
console.log(window.globalConst); // Output: undefinedExample: class Declarations#
class GlobalClass {}
console.log(window.GlobalClass); // Output: undefinedThis consistency makes sense: let, const, and class are all block-scoped and part of ES6’s modern scoping model, which prioritizes lexical scoping over the legacy global object model.
6. Practical Implications and Common Pitfalls#
Understanding whether a global variable is a property of the global object has real-world implications:
1. Avoiding Naming Collisions#
var variables risk overwriting existing global object properties (e.g., window.alert). With let, this is impossible:
var alert = "Oops, overwrote window.alert!";
window.alert("Hello"); // Throws an error (alert is now a string, not a function)
// With let, no collision:
let alert = "This is safe";
window.alert("Hello"); // Still works! (window.alert is unchanged)2. Checking for Global Variables#
If you need to check if a global variable exists, using window.variableName works for var but not for let/const. Instead, use a typeof check:
// For var:
var myVar;
console.log("myVar" in window); // true
// For let:
let myLet;
console.log("myLet" in window); // false (incorrect check)
console.log(typeof myLet !== "undefined"); // true (correct check)3. Node.js vs. Browsers#
In Node.js, the global object is global, but top-level var declarations in modules do NOT attach to global (modules have their own scope). However, in non-module scripts (e.g., run with node script.js), var behaves like in browsers:
// In a Node.js script (not a module):
var nodeVar = "var in Node";
console.log(global.nodeVar); // Output: "var in Node"
let nodeLet = "let in Node";
console.log(global.nodeLet); // Output: undefined7. Conclusion#
To answer the original question: No, let statements in the global scope do not create properties on the global object.
varin global scope: Attaches to the global object (e.g.,window.varName).let,const, andclassin global scope: Stored in the global declarative environment, not as properties of the global object.
This distinction is rooted in ES6’s design to separate legacy global object behavior (for var and function declarations) from modern lexical scoping (for let, const, and class). Understanding this helps avoid bugs, naming collisions, and confusion when working with global variables.