Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Confused about let and var

15 views
Skip to first unread message

Erwin Moller

unread,
Sep 23, 2016, 4:20:48 AM9/23/16
to
On this page:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

you can find the following example:

=============================================================
var list = document.getElementById("list");

for (let i = 1; i <= 5; i++) {
let item = document.createElement("li");
item.appendChild(document.createTextNode("Item " + i));

item.onclick = function (ev) {
console.log("Item " + i + " is clicked.");
};
list.appendChild(item);
}



The example above works as intended because the five instances of the
(anonymous) inner function refer to five different instances of the
variable i. Note that it does not work as intended if you replace let
with var, since all of the inner functions would then return the same
final value of i: 6. Also, we can keep the scope around the loop cleaner
by moving the code that creates the new elements into the scope of each
loop.

=============================================================

This example of 'let' is confusing to me.

Compare it to the following code:

var list = document.getElementById("list");
let i = 1;
while (i <= 5) {
let item = document.createElement("li");
item.appendChild(document.createTextNode("Item " + i));

item.onclick = function (ev) {
console.log("Item " + i + " is clicked.");
};
list.appendChild(item);
i++;
}

In the above code 'let' behaves as 'var' and all clicks will result in
"Item 6 is clicked".

(I am not sure I should use 'closure' in this context, but the code
behaves that way.)

So what happens differently between these two codepieces?
1) let i = 1;
while (i <= 5) {...


and

2) for (let i = 1; i <= 5; i++) {...


Is the use of 'let' in the for-loop considered INSIDE the block?
(Because literally, it is not).

Thanks for your time!

Regards,
Erwin Moller


--
"That which can be asserted without evidence, can be dismissed without
evidence."
-- Christopher Hitchens

Ben Bacarisse

unread,
Sep 23, 2016, 2:27:03 PM9/23/16
to
Erwin Moller <erwinmol...@xs4all.nl> writes:

> On this page:
> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
>
> you can find the following example:
>
> =============================================================
> var list = document.getElementById("list");
>
> for (let i = 1; i <= 5; i++) {
> let item = document.createElement("li");
> item.appendChild(document.createTextNode("Item " + i));
>
> item.onclick = function (ev) {
> console.log("Item " + i + " is clicked.");
> };
> list.appendChild(item);
> }
<snip>
> This example of 'let' is confusing to me.
>
> Compare it to the following code:
>
> var list = document.getElementById("list");
> let i = 1;
> while (i <= 5) {
> let item = document.createElement("li");
> item.appendChild(document.createTextNode("Item " + i));
>
> item.onclick = function (ev) {
> console.log("Item " + i + " is clicked.");
> };
> list.appendChild(item);
> i++;
> }
>
> In the above code 'let' behaves as 'var' and all clicks will result in
> "Item 6 is clicked".
>
> (I am not sure I should use 'closure' in this context, but the code
> behaves that way.)
>
> So what happens differently between these two codepieces?
> 1) let i = 1;
> while (i <= 5) {...
>
>
> and
>
> 2) for (let i = 1; i <= 5; i++) {...
>
> Is the use of 'let' in the for-loop considered INSIDE the block?

That's one way to look at it, yes. The actual rules are, as always with
ECMA 262, very fussy indeed, but the 6th edition didn't just introduce
'let' declarations, it it also defined a whole new form of 'for'
statement in which the first part in the ()s is a 'LexicalDeclaration'.
The semantics of the new form make it plain that every loop execution
occurs in a new environment with a fresh binding for the lexical
variables.

> (Because literally, it is not).

Curiously, in C, a for statement constitutes a block which starts at the
for keyword so there is a block even when there is no compound statement
controlled by the for. ECMAScript does not do that, but the rules have
the same effect.

--
Ben.

Thomas 'PointedEars' Lahn

unread,
Sep 24, 2016, 12:12:08 PM9/24/16
to
Erwin Moller wrote:

> […] Also, we can keep the scope around the loop cleaner by moving the code
> that creates the new elements into the scope of each loop.

Not only the loop, but also the *Block* statement associated with the loop
statement. See below.

> […]
> Compare it to the following code:
>
> var list = document.getElementById("list");
> let i = 1;
> while (i <= 5) {
> let item = document.createElement("li");
> item.appendChild(document.createTextNode("Item " + i));
>
> item.onclick = function (ev) {
> console.log("Item " + i + " is clicked.");
> };
> list.appendChild(item);
> i++;
> }
>
> In the above code 'let' behaves as 'var' and all clicks will result in
> "Item 6 is clicked".
>
> (I am not sure I should use 'closure' in this context, but the code
> behaves that way.)

“Closure” is the correct term here.

<https://en.wikipedia.org/wiki/Closure_(computer_programming)>

> So what happens differently between these two codepieces?
> 1) let i = 1;
> while (i <= 5) {...
>
>
> and
>
> 2) for (let i = 1; i <= 5; i++) {...
>
>
> Is the use of 'let' in the for-loop considered INSIDE the block?

“var” does variable scoping based on the VariableEnvironment of execution
contexts. “let” enforces *lexical* *block* scoping or, IOW, scoping “to the
running execution context’s LexicalEnvironment” (ECMAScript Language
Specification [ES] 6.0 and 7.0, § 13.3.1).

Different to a declaration with “var”, a variable declared with “let” is
only defined in the same block and inner blocks (like e.g. Python¹). If you
declare a variable with “let” outside of a block, then it works like “var”
inside the block because it is bound to the outer block’s lexical
environment.

_______
¹ Different to “var” and to Python, there can only be one “let” declaration
for the same variable in the same block/LexicalEnvironment. Otherwise it
is a syntax error (“SyntaxError: Identifier 'foo' has already been
declared”). Note that this is purely syntax-related: You can have
one declaration with “let” for the same variable inside a loop, but not
more. [AFAIK, Python does not have a variable declaration without
explicit initialization, so there is no obvious difference between a
declaration with initialization and a simple assignment. PHP and a
number of other scripting languages exhibit the same issue.]

> (Because literally, it is not).

The difference between the “while” statement and the “for” statement
(variants that do not contain the “in” or “of” keyword) is that the latter
has an initialization part that allows for variable declarations.
Therefore, there is a special “for” statement where with a *lexical*
declaration the variable can be bound to the “for” statement instead of the
adjacent block, and still, since *semantically* the adjacent block has to
have access to the variables declared in the “for” statement, it works as if
the “for” statement would create a block of its own (ES 6.0 and 7.0,
§ 13.7.4).

A most interesting thing happens when you use “for (let …)” and move the
post-increment operation into the block: you end up with the console saying
“Item 2 is clicked” when you clicked “Item 1” and so on. Intuitively you
would think that an increment in the “for” statement or in the adjacent
block were equivalent; but that is not so if the variable was declared with
“let” in the “for” statement.

If you find “let” confusing, I suggest that you do not use it. That would
have the additional advantage of writing backwards-compatible code.

--
PointedEars
FAQ: <http://PointedEars.de/faq> | SVN: <http://PointedEars.de/wsvn/>
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-matrix>
Please do not cc me. / Bitte keine Kopien per E-Mail.
0 new messages