Understanding where Scope Chains end is an important part of scoping. All
Javascript runtimes must implicitly create a Global Scope object (window
in
the browser, global
in node), which sits at the top of every scope chain:
(global)
↑
|
someFunc()
↑
/ \
/ \
/ \
inner() inner2()
↑
|
foo()
In Scopes we covered how usage of var
or let
dictates the scope of the
variable being defined. When assigning a variable without using either of var
,
let
, etc, the variable is assumed to exist in an outer scope.
The javascript runtime follows these steps to assign a variable:
- Search within the current scope.
- If not found, search in the immediately outer scope.
- If found, go to 6.
- If not found, repeat 2 and 3 until the Global Scope is reached.
- If not found in Global Scope, create it (on
window
/global
objects). - Assign the value.
In this way, it is possible to accidentally define a global variable (step 5).
Consider the following example:
function someFunc() {
var scopedVar = 1;
function inner() {
foo = 2;
}
}
Note the lack of var
or let
, etc for foo = 2
. The Javascript runtime will
follow the above algorithm, first checking the scope of inner()
, then of
someFunc()
, then finally the Global Scope. Step 5 is then executed, so foo
becomes a variable in the Global Scope (window.foo
/ global.foo
).
Phrased another way: By accidentally forgetting to use var
, the variable foo
which otherwise would have been only within the lexical scope of inner()
is
now available to be modified by any scope. So, someFunc()
now has access
where the developer may have meant for it not to.
Remember: Only inner scopes can access variables of outer scopes. In this case
the someFunc()
scope is an inner scope of the Global Scope, allowing access of
foo
to someFunc()
.
A variable is created in a 'Step 0)' of the above algorithm: When var
or let
is used. The variable is assigned to the correct scope, then execution moves on,
and any assignments to that variable follow the above algorithm.
It is perfectly valid to define two different variables, in different scopes, with the same name:
function someFunc() {
var foo = 1;
}
function anotherFunc() {
var foo = 2;
}
It is also valid to do this in nested scopes:
function someFunc() {
var foo = 1;
function inner() {
var foo = 2;
}
}
This is called Shadowing. The foo
inside inner()
is said to Shadow the foo
inside someFunc
.
Shadowing means that the inner()
scope only has access to its own foo
. There
is no way for it to access the foo
defined in someFunc()
.
This can also be an accidental source of bugs, especially when there is deep nesting, or long functions.
Starting with your solution from the previous lesson, assign a value to the global variable
quux
inside foo()
(don't use var
or let
). Create a shadow variable in of quux
inside zip()
. The value in the global variable quux
has to be different than the
value of quux
inside zip()
.
Once complete, execute sccjs verify <your-file.js>
to verify your
solution.