Reputation: 50732
When I run the following code written using ES6 let
:
function doSmth(){
age=27;
}
let age;
doSmth();
console.log(age);
I get the output correctly as 27.
Now I am a bit confused as to how this works. Specifically, I have read is that let
has a block scope.
So, how is it that let age
, which is declared outside the function doSmth
, is the same variable inside the function doSmth
?
Is that something to do with global scope, but if yes, then what role does block scope have exactly ?
Upvotes: 1
Views: 390
Reputation: 370679
"Let has block scope" doesn't mean that the variable must be defined in the same block - it means that the level at which the variable is defined can be a block.
In contrast, the level at which a var
variable is defined cannot be a block, unless that block is also a function block. If a var
variable is initialized inside a non-function block, it will be hoisted out to the nearest outer block which is also a function block.
No matter whether a variable is declared with var
, let
, or const
, it will be referencable anywhere else inside the same block in which it was declared, including inside child blocks. (For let
and const
, the line that declares the variable, eg const foo =
, must also have run for a reference to foo
to work without throwing.)
Another way of looking at it is: if a variable is referenced somewhere, the interpreter looks for whether it's initialized in the current block. If so, that's the binding that's used. Otherwise, it looks in the next outer block to see if the variable is initialized there - if so, that's the binding that's used. And so on. If it gets all the way to the top level, and no binding has been found, and it's not a property of the global object, that reference will throw a ReferenceError
.
In your code, the age
variable happens to be global, since it's defined on the top level, but that doesn't really matter - the code would work the same if age
was just an an outer block, but not on the top level, eg:
function foo() {
function doSmth() {
age = 27;
}
let age;
doSmth();
console.log(age);
}
foo();
Upvotes: 3
Reputation: 707218
Now I am a bit confused as to how this works. Specifically, I have read is that
let
has a block scope.
Yes, let
has block scope. But a block such as inside your doSmmth()
function has access to ALL the variables in parent scopes that have been defined or hoisted at the time of the function execution.
So, let age;
is in the parent scope and has been defined by the time doSmth()
executes. Therefore the interpreter tasked with looking up a variable named age
finds it in the parent scope just fine.
So, how is it that
let age
, which is declared outside the functiondoSmth
, is the same variable inside the function doSmth?
Here's how the interpreter works in this regard. Your code executes in these steps:
function doSmth()
definition is encountered. Functions are hoisted to the top of the containing function scope so that definition is immediately added to this scope object. let age
is also encountered in the parsing and is added to the parsed scope, but it is marked in a way that it will not yet be accessible to code.let age;
is encountered. This marks the age
variable which was previous put into this scope at parsing time as now available for code to use.doSmth()
. This pushes a return address on the callstack and as it sets up to execute doSmth()
, it creates a new function scope.age = 27
inside that function. The interpreter looks in the local scope for a symbol named age
. It doesn't find one. So, it goes up the scope chain and looks in the parent scope. It finds a matching symbol and uses that one.doSmth()
was and executes the console.log(age)
.age
and finds it and uses it.So, the main keys here that it sounds like you may be confused by are:
If a symbol isn't found in the local scope, then the parent scopes are searched for the symbol name.
let
does create a block-scoped symbol, but it can still be accessed by any child functions also declared in that scope.
In this particular code, there wouldn't be any difference between let
and var
and it isn't hoisting that makes this work. It's parent scopes.
Upvotes: 1
Reputation: 2587
Your code will go through two phases.
In first phase you will declare function doSmith
and age
variable. Before executeion phase JS engine knows what is age
variable and what is doSmith
.
In next phase i.e. execution phase, execution will occur,
doSmth();
console.log(age);
will get executed. Hence in age variable is accessible to function doSmith
in execution phase.
Upvotes: 0
Reputation: 2128
In your code, both doSmth and age are in global scope. Windows for browser and module object for node. So they basically are in the same scope.
Upvotes: 0