ref – http://www.w3schools.com/js/js_scope.asp
ref – https://stackoverflow.com/questions/17279437/lexical-scope-closures-in-javascript
ref – http://adripofjavascript.com/blog/drips/emulating-block-scope-in-javascript.html
ref – https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/
JavaScript has two scopes: global and local.
variables declared outside of your function…
Any variable declared outside of a function belongs to the global scope, and is therefore accessible from anywhere in your code.
variables declared inside of your function…
Each function has its own scope, and any variable declared within that function is only accessible from that function and any nested functions.
Because local scope in JavaScript is created by functions, it’s also called function scope. When we put a function inside another function, then we create nested scope.
Currently, JavaScript, unlike many other languages, does not support block level scoping. This means that declaring a variable inside of a block structure (like a for loop), does not restrict that variable to the loop. Instead, the variable will be accessible from the entire function.
It’s worth noting that the upcoming ECMAScript 6 will support block level scopes via the let keyword.
Lexical Scope
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var locales = { europe: function() { // The Europe continent's local scope var myFriend = "Monique"; var france = function() { // The France country's local scope var paris = function() { // The Paris city's local scope console.log(myFriend); }; paris(); }; france(); } }; locales.europe(); |
In Paris, I want to print “myFriend”. So I start my searching from there. When I can’t find her in Paris I go one level up and expand my searching in all of France. But again, she is not there. Next, I expand my searching again by going another level up. Finally, I found her in Italy, which in our case is the local scope of Europe.
In the previous example my friend Monique is represented by the variable myFriend. In the last line we call the europe() function, which calls france(), and finally when the paris() function is called, the searching begins. The JavaScript interpreter works from the currently executing scope and works it way out until it finds the variable in question. If the variable is not found in any scope, then an exception is thrown.
This type of look up is called lexical (static) scope.
The static structure of a program determines the variable scope. The scope of a variable is defined by its location within the source code, and nested functions have access to variables declared in their outer scope. No matter where a function is called from, or even how it’s called, its lexical scope depends only by where the function was declared.
Shadowing
If you declare a local variable and a global variable with the same name, the local variable will take precedence when you use it inside a function. This type of behavior is called shadowing. Simply put, the inner variable shadows the outer.
That’s the exact mechanism used when a JavaScript interpreter is trying to find a particular variable. It starts at the innermost scope being executed at the time, and continue until the first match is found, no matter whether there are other variables with the same name in the outer levels or not.
1 2 3 4 5 6 7 8 9 10 |
var test = "I'm global"; function testScope() { var test = "I'm local"; console.log (test); } testScope(); // output: I'm local console.log(test); // output: I'm global |
Additional Material
To put it simply, a scope is the lifespan of a variable. You see, every variable is born, lives and dies. The beginning of a scope marks the time the variable is born and the end of the scope marks the time it dies.
1 2 3 |
if (true) { let v = null; } |
The scope in the previous example was a block scope. It’s just a block of code. Hence the name. Block scopes are immediately executed.
1 2 3 |
function Foo() { let name = "default"; } |
Function scopes on the other hand are templates of block scopes. As the name suggests a function scope belongs to a function. However, more precisely, it belongs to a function call. Function scopes do not exist until a function is called.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var x = 10 function inc(x) { print(x + 1); } // function scope exists, x is 3 + 1. // Within function scope, prints 4 inc(3); // x is from global scope, prints 10 print(x); // function scope exists after function being called, x is 7. // Within function scope, prints 8 inc(7); |
Using var, JavaScript only has function scopes. Using let, it has block scopes.
If we are not using let, we can use IIFE to emulate block scoping.
Let’s take a look at an example.
Simulating block scoping with IIFE (Implicit Invoked Function Expression)
In the global scope, we have variables avatar, element, and an array elements.
Then we have a for loop, which is a block. However, before ‘let’, JS do not have block scoping.
Any variables declared inside a block, is scoped to the nearest function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var avatar = "Ang"; var element = "Air"; var elements = [ "Air", "Earth", "Fire", "Water" ]; for (var i = 0; i < elements.length; i++) { var element = elements[i]; console.log(avatar + " has mastered " + element); } // Outputs: "Ang's primary element is Water" console.log(avatar + "'s primary element is " + element); |
In JavaScript, variables declared with var are scoped to the nearest parent function.
Hence in our case, when we declare “var element”, it gets scoped to the nearest parent function.
Since element was already declared, the element in the for loop block reference the element in its parent scope. That’s why after the for loop,
var element will have “Water”.
And the output would be: “Ang’s primary element is Water”.
Solution
What we expect is element to be “Air”. Hence, in order for this to happen, we must imitate block scope by using
IIFE like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var avatar = "Ang"; var element = "Air"; var elements = [ "Air", "Earth", "Fire", "Water" ]; for (var i = 0; i < elements.length; i++) { (function() { var element = elements[i]; console.log(avatar + " has mastered " + element); })(); } // Outputs: "Ang's primary element is Air" console.log(avatar + "'s primary element is " + element); |
So what we did here is that in the for loop. We first defined an anonymous function.
When the function is defined, a closure is created. It can reference its own scope, parent’s scope, and global scope.
For example, the elements array can be accessed as it is its parent’s scope.
Notice the anonymous function has () at the end. This is executing the anonymous function. We we execute the function, scope is created. This means at the start of the function execution, variables is alive, and when the function exists, the variable die.
Since variables declared with var are scoped to the nearest parent function.
The var “element” is scoped to our anonymous function.
That’s why var element only exists here. For every pass of the loop, we have a scope with its own variable element.
That element references global scope var “elements”.
It logs it, then exits.
When we are done with the for loop, our scope is global, where element still has data “Air”.
The Lifetime of JavaScript Variables
The lifetime of a JavaScript variable starts when it is declared.
Local variables are deleted when the function is completed.
Global variables are deleted when you close the page.
local scope
Variables declared within a JavaScript function, become LOCAL to the function.
Local variables have local scope: They can only be accessed within the function.
1 2 3 4 |
function myFunction() { var carName = "Volvo"; // code here can use carName } |
Local variables are created when a function starts, and deleted when the function is completed.
global scope
A variable declared outside a function, becomes GLOBAL.
A global variable has global scope: All scripts and functions on a web page can access it.
auto global
Automatically Global
If you assign a value to a variable that has not been declared, it will automatically become a GLOBAL variable.
This code example will declare carName as a global variable, even if it is executed inside a function.
1 2 3 4 5 6 |
// code here can use carName function myFunction() { carName = "Volvo"; // code here can use carName } |
Global Variables in HTML
With JavaScript, the global scope is the complete JavaScript environment.
In HTML, the global scope is the window object: All global variables belong to the window object.
1 2 3 4 |
// code here can use window.carName function myFunction() { carName = "Volvo"; } |