Reputation: 50291
This example is taken from mdn and it throws error because
The instruction let n of n.a is already inside the private scope of the for loop's block, hence the identifier "n.a" is resolved to the property 'a' of the 'n' object located in the first part of the instruction itself ("let n"), which is still in the temporal dead zone since its declaration statement has not been reached and terminated.
function go(n) {
for (let n of n.a) {
console.log(n);
}
}
go({
a: [1, 2, 3]
});
Now I changed the variable name from n
to k
and it works. But according to mdn
let k
will be still in temporal dead zone. Then how come this does not throws the error but logs the value?
function go(n) {
for (let k of n.a) {
console.log(k);
}
}
go({
a: [1, 2, 3]
});
Upvotes: 1
Views: 113
Reputation: 138257
It is a ReferenceError in the first case because the spec wants to prevent confusion if a variable name is used on both sides of the of
. In theory even the first version could work, then this:
for(let n of n.a) { /*body*/ }
could be executed as:
const iterator = n.a[Symbol.iterator](); // the iterator is initialized in the parent scope
let done = true;
while(!done) {
let n;
({ value: n, done } = iterator.next()); // "n" is initialized in the local loop scope.
/* body */
}
But as that would create confusion if n.a
on the right side would not refer to the n
on the left-side, the authors of the spec added another scope, to prevent that variables of the loop are used in the iterator: All local variables also get declared in another scope, that is only used to initialize the iterator:
{ // another local scope (named TDZ in the spec)
const iterator = n.a[Symbol.iterator](); // "n" cannot be used here, as it was not yet initialized
let done = false;
let n; // this is just to prevent "n" from being used in the line above
while(!done) {
let n;
({ value: n, done } = iterator.next());
}
}
Relevant section of the spec:
13.7.5.12 Runtime Semantics: ForIn/OfHeadEvaluation
[...]
b. Let TDZ be NewDeclarativeEnvironment(oldEnv). [...] d. For each string name in TDZnames, do i. Perform ! TDZEnvRec.CreateMutableBinding(name, false). e. Set the running execution context's LexicalEnvironment to TDZ. 3. Let exprRef be the result of evaluating expr
whereas expr is n.a
in this case, and TDZnames contains n
.
Upvotes: 1
Reputation: 85767
for (let n of n.a) {
// ^^^^^^
The temporal dead zone of this statement is the underlined of n.a
part. Since it contains a reference to n
, the variable being declared (by let n
), this is an error.
for (let k of n.a) {
// ^^^^^^
In this example the temporal dead zone still consists of n.a
, but we're now declaring a different variable, k
. Since the expression in the dead zone doesn't use k
, it's fine.
Upvotes: 1
Reputation: 14679
The "n being in TDZ" refers to the second n in the loop declaration, "let n of n.a". With "for (let k of n.a)" that problem doesn't exist: the only time k is referenced is inside the loop.
Upvotes: 0
Reputation: 1074138
But according to mdn
let k
will be still in temporal dead zone.
No, by the time you've used it in your code (console.log(k)
), it's been initialized with the value being iterated for that loop. It's not in the TDZ anymore.
The TDZ problem with the let n
version is here:
for (let n of n.a) {
// -----------^
At that point, the n
declared with let
has shadowed the n
parameter. It's reserved and in the TDZ, but the code is trying to use it (not the n
parameter) in of n.a
. That's not a problem with your k
version, because you don't reference k
there.
Upvotes: 2