getify / You-Dont-Know-JS

A book series on JavaScript. @YDKJS on twitter.
Other
179.37k stars 33.48k forks source link

"scope & closures": Block Scoping Revisited - clarification of scope created by let (question) #529

Closed bl4de closed 9 years ago

bl4de commented 9 years ago

Hi,

I've got a question about scope created by let.

We've got an example (similar as in YDKJS):

"use strict";

for (let a = 1; a < 10; a++) {
    setTimeout( function() {
        console.log(a);
    }, 0);
}

Works perfect, closure in setTimeout() has access to valid a in each iteration as is defined inside scope created by let.

But let's do something like this:

"use strict";
function foo() {
    {
        let b = 10;
        for (let a = 1; a < 10; a++) 
        {
            console.log("in loop: ", a);
            fn();
        }
    }
}

foo();

console.log(a);  // undefined
console.log(b);  // Uncaught ReferenceError: b is not defined

function fn() {
    console.log("in fn(): ", a);
}

I define fn() outside of scope created by let (in for statement). Also there's one more block scope created by outside brackets. a should not be visible, right? And everything is enclosed in foo() which is created yet another scope.

And the result is:

in loop: 1 closure2.js:20 in fn(): 10 closure2.js:7 in loop: 2 closure2.js:20 in fn(): 10 closure2.js:7 in loop: 3 closure2.js:20 in fn(): 10 closure2.js:7 in loop: 4 closure2.js:20 in fn(): 10 closure2.js:7 in loop: 5 closure2.js:20 in fn(): 10 closure2.js:7 in loop: 6 closure2.js:20 in fn(): 10 closure2.js:7 in loop: 7 closure2.js:20 in fn(): 10 closure2.js:7 in loop: 8 closure2.js:20 in fn(): 10 closure2.js:7 in loop: 9 closure2.js:20 in fn(): 10 closure2.js:15 10 closure2.js:16 Uncaught ReferenceError: b is not defined

fn() has an access to a as is called inside the scope, where a is defined (right?). But how a might be accessible outside of all scopes which surround it? (this line with console.log(a), where there's undefined - as I was expecting Reference Error, the same as for b)

And why a in each iteration, printed by fn(), behaves as if fn() was declared as closure, similar as in your example from "Loops + Closure" chapter?

Is there's something I didn't I understand from scopes?

getify commented 9 years ago

IIUC the only explanation for this is that in whatever environment you're trying this in, there's already an a in the outer scope, diff from the a in the loop. Or whatever implementation you're running in has a massive bug. a most definitely should not be accessible in that scope.

bl4de commented 9 years ago

This is totally insane, but you're correct as usual.

I've tried code above in my Chrome Dev Tools (created some small snippet for this). Yeah, and there was an a in Chrome Dev Tools console defined an hour or two earlier today :/

In fresh environment, the code collapsed right after first call on fn(), which was of course expected.

I am sorry for this issue, I should check this before I've created one. Shame on me.

getify commented 9 years ago

No worries. :) Happens all the time, actually.