# Why `a = 10` Isn't Hoisting: Implicit Globals in JavaScript

Look at this code:

```javascript
a = 10;
console.log("This is a hoisted variable: " + a); // 10
```

No `var`, `let`, or `const` and it still works. Many developers call this hoisting, but that is not what is happening. It is an **implicit global**, and understanding the difference tells you a lot about how JavaScript stores variables in memory.

In this post, we will look at where `a` actually lives, how it differs from `var`, `let`, and `const`, and why strict mode exists to stop it.

## 1\. It is an implicit global, not hoisting

When the engine runs `a = 10`, it looks for `a` up the scope chain. If it does not find it (and you are in sloppy mode), it creates a property on the **global object**: `window` in browsers, `globalThis` or `global` in Node.

```javascript
a = 10;
console.log(globalThis.a); // 10
console.log(window.a);     // 10 (browser)
```

Hoisting applies to *declarations* and happens at parse time. Here there is no declaration for the engine to hoist, so the binding only comes into existence when the line executes:

```javascript
console.log(a); // ReferenceError: a is not defined
a = 10;
```

## 2\. Hoisting vs implicit global

Compare that with `var`, which really is hoisted. The declaration is registered before any code runs, so reading it early gives `undefined` instead of an error:

```javascript
console.log(b); // undefined
var b = 10;
```

## 3\. Where each kind of global lives

| Declaration | Stored in | On `window`? | Deletable? |
| --- | --- | --- | --- |
| `a = 10` (undeclared) | Global object property | Yes | Yes (`delete a`) |
| `var a = 10` | Global object property | Yes | No (non-configurable) |
| `let` / `const` / `class` | Global lexical environment (declarative record) | No | No |

So `let` and `const` globals live in a separate scope that sits alongside the global object rather than on it.

## 4\. The global lexical environment

The phrase "declarative record" is spec terminology for *where a scope's variables are stored*. The global scope is built from **two** records stacked together, and together they form the global lexical environment.

```plaintext
Global Environment
├── Declarative record → let, const, class        (hidden table)
└── Object record      → var, functions, a = 10   (window properties)
```

**Object Environment Record:** backed by the global object. Variables here are literally properties on `window` or `globalThis`. `var` declarations, function declarations, and implicit globals go here.

**Declarative Environment Record:** the engine's private name-to-value table. There is no object behind it, so nothing like `window.x` exists for those names, yet normal scope lookup still finds them:

```javascript
var v = 1;
let l = 2;

console.log(window.v); // 1
console.log(window.l); // undefined
console.log(l);        // 2
```

## 5\. Lookup order and conflicts

At global level the engine checks the declarative record first, then the object record. Even though they are different records, the spec forbids a `let` from redeclaring a `var` name in the same global scope:

```javascript
var x = "from var";
let x = "from let"; // SyntaxError: already declared
```

You can, however, shadow a window property that your script did not declare:

```javascript
let name = "mine";
console.log(name);        // "mine" (declarative record wins)
console.log(window.name); // the browser's built-in value
```

## 6\. Why the split exists

`let` and `const` arrived in ES6 with stricter semantics: a temporal dead zone, no redeclaration, and no deleting. Putting them on the global object would have polluted `window` further and collided with legacy behavior. A separate declarative record gave them clean semantics without breaking old code.

## 7\. Strict mode

```javascript
"use strict";
a = 10; // ReferenceError: a is not defined
```

Strict mode, which also covers all ES modules and class bodies, blocks implicit globals entirely. That matters because they are a classic bug source: a typo silently creates a new global, and any other code can overwrite it.

## Takeaways

*   `a = 10` is an implicit global created at runtime on the global object, **not** hoisted.
    
*   `var` is hoisted and lives on the global object; `let`, `const`, and `class` live in the hidden declarative record.
    
*   Always declare variables with `let` or `const`.
    
*   Add `"use strict"` (or use modules) so accidental globals fail loudly.
