Frontend Development 10 min read

Why Does let Appear Not to Be Hoisted? Unraveling JavaScript’s Variable Lifecycle

This article chronicles the author’s two‑month journey of understanding JavaScript’s let declaration, exploring its block scope, temporal dead zone, per‑iteration bindings, and the nuanced differences from var and function hoisting, while debunking common misconceptions with code examples and spec references.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Why Does let Appear Not to Be Hoisted? Unraveling JavaScript’s Variable Lifecycle

1 Preface

I spent two months piecing together my understanding of let, writing this article to help others who are self‑studying JavaScript.

2 Getting to Know let

From MDN I learned three key points:

let creates block‑scoped variables;

let cannot redeclare an existing variable;

let has a temporal dead zone and is not hoisted.

3 First Doubt

When using a

for

loop, the following code with

var

prints five times the number 5:

<code>// code snippet 1
var liList = document.querySelectorAll('li');
for (var i = 0; i < liList.length; i++) {
  liList[i].onclick = function() { console.log(i); };
}</code>

Replacing

var i

with

let i

prints 0, 1, 2, 3, 4 as expected:

<code>// code snippet 2
var liList = document.querySelectorAll('li');
for (let i = 0; i < liList.length; i++) {
  liList[i].onclick = function() { console.log(i); };
}</code>

The original reasoning fails because only one

i

is declared, so after the loop finishes its value becomes 5.

MDN’s example avoids this by introducing a hidden

j

that captures the current

i

value.

4 Second Doubt

A StackOverflow question asked whether let is hoisted. One high‑voted answer claimed all declarations are hoisted, citing code that appears to access

x

before its declaration.

<code>x = "global";
(function() {
  x; // not "global"
  var/let/... x;
})();
{ 
  x; // not "global"
  let/const/... x;
}</code>

Further research revealed contradictory MDN edits and the ES spec’s mention of “var/let hoisting”.

TC39 member Rick Waldron clarified that the term “hoisting” is non‑normative for let/const: they do not hoist like var or function declarations.

You're misunderstanding the intention of that (non‑normative) Note. let and const do not hoist as var and function decls do.

5 The Story Continues

Understanding hoisting requires distinguishing creation, initialization, and assignment of variables.

var creates the variable and initializes it to

undefined

before code runs, then assigns values during execution.

function declarations are created, initialized, and assigned before execution.

let creates the binding during the creation phase, but initialization occurs when the declaration is evaluated; before that the variable is in the temporal dead zone.

<code>{
  let x = 1;
  x = 2;
}</code>

This explains why accessing

x

before its

let

declaration throws a ReferenceError.

Similarly,

const

has creation and initialization only, with no later assignment.

The following diagram (image) summarizes the lifecycle of var, let, const, and function declarations.

5 Is It Over?

When

let x = x

fails, the variable remains in the created state forever, never initialized, and stays in the temporal dead zone, effectively halting further execution.

These observations illustrate the subtle but important differences in JavaScript’s variable handling.

frontendJavaScriptletES6variable scopehoistingtemporary dead zone
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.