2

The following code will output "1." However, shouldn't the keyword "let" not make x a global variable, thus making it invisible to das()? let is supposed to limit the scope of variables to only the block where they're declared, yet here, I'm seeing an inner function have access to a "let" variable, even though x was declared outside its scope. How is that possible?

function letTest() {
 function das () {
  console.log(x);
// How does this function have access to a let variable declared outside its scope?
 }

 let x = 1;
 das();
}
letTest();
GTS Joe
  • 3,612
  • 12
  • 52
  • 94
  • I think it is because `das` is within the block that `x` is defined in.. just inside 2 blocks, but one of them also surround the `let` so it is fine – Max Dec 26 '16 at 23:45
  • 2
    `x` does not have to be *global* in order for it to be visible in the nested function; it just has to be in either the local scope or some outer scope. A `let` declaration does not limit visibility only to the local scope; it's the local scope plus any nested scopes. – Pointy Dec 26 '16 at 23:47
  • 3
    To put it another way, the code inside that nested function **is** part of the block that contains the `let`. – Pointy Dec 26 '16 at 23:48
  • Maybe I'm wrong but I don't think this has anything to do with hoisting. – Pointy Dec 26 '16 at 23:53

7 Answers7

6

Here's a way of thinking about how let works:

  1. Start from the let.
  2. At the same nesting level, working back/up through the source code, find the first {.
  3. Now from the let find the corresponding }.

That gives you the scope wherein the variable will be visible. If a function definition appears in that scope, fine; the variable is visible to code in that function.

Now, what's a little bit weird is that in your example the variable looks like it's used in the scope before it's declared. That's where the fact that the reference appears before the declaration becomes a little more interesting.

Normally, if code in a scope refers to a let-declared variable before the let actually happens, that's an error. However, that's a runtime thing, not a syntax thing. In your case, at runtime the let will have "happened" by the time the nested function is called.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 2
    So "let" limits outer scope, but not inner scope? – GTS Joe Dec 26 '16 at 23:54
  • 1
    @GTSJoe I just commented on another answer. There's no such thing as "inner scope". – Pointy Dec 26 '16 at 23:54
  • 1
    @GTSJoe to put it another way, the `{ }` for the nested function body are no different than the `{ }` for a `for` loop that appears in the scope. – Pointy Dec 26 '16 at 23:55
  • 1
    @GTSJoe also see my update; I realized I had left off some nuance to the way things work with `let`. – Pointy Dec 27 '16 at 00:00
1

let limits the scope of the variable to the current block (in this case that is the same as the letTest function).

The das function is declared inside that scope, so it has access to anything in that scope (including x).

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
0

The thing is that you are not mutating the value of x, in your case you are just logging it to the console and they are all nested under one scope which does not limit the inner function, so your result is expected.

Now if you did something like this

function letTest() {
    function das () {
        console.log(x);
// How does this function have access to a let variable declared outside its scope?
    }
    let x = 1;
    function change(){
       x = x + 1;
    }

    change();
}
letTest();

Now you are trying to change the value of x and the compiler would complain.

ekeith
  • 644
  • 1
  • 10
  • 33
0

As described here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let it provides access within the block scope. As your das() execution and x declaration are in the same block scope it will have access.

A good example is in the switch case below, using var x = 'x' in case 1 will cause it to attach to the function scope and accessible in the console.log() but when trying to reference the bar variable for logging it throws a reference exception. But still works in the default case.

const example = (foo) => {
    switch(foo) {
        case 0:
            break;
        case 1:
            let bar = 'bar';
            var x = 'x';
            break;
        default:
            console.log(bar);
            break;
    }

    console.log(x);
    console.log(bar);
}

example(1);
nickmcblain
  • 155
  • 1
  • 2
  • 12
0

The function is declared in the same block as x, so the code inside the function has access to x.

If you don't want that, you can always add a new block:

function letTest() {
  function das () {
    console.log(x); // ReferenceError
  }
  { // New block
    let x = 1;
    das();
  }
}
letTest();
Oriol
  • 274,082
  • 63
  • 437
  • 513
0

@GTS Joe,

the let x is local to the letTest() context, therefore das() is simply accessing its xvariable. Nothing strange is going on there. So, 'no' - it's not becoming global.

No matter how deep the context of the variable call has been nested, there's always a look-up JS principle, that will follow on a failed look around and will continue to look up for this variable name all the way through the global scope, and only then - when nothing's found - it will cause a reference error.

And that - only because there's no look below JS principle - the so called closures are possible.

Bekim Bacaj
  • 5,707
  • 2
  • 24
  • 26
-1

It is not outside the scope. x and das() are defined in the same block scope. Anything else within that block has access to x. That includes any nested functions, for loops, while loops and if conditionals. That includes elseif and else that are inside the if conditional.

kiner_shah
  • 3,939
  • 7
  • 23
  • 37
Agent86
  • 1
  • 2