In modern days more and more applications are made online and as we know JavaScript is pivotal for any web application either simple or rich (RIA). Understanding JavaScript is never more important and JavaScript scope is key to writing bullet-proof code for software professionals who work with web applications mostly.
In this article I am going to talk through what you should know about JavaScript Scoping. We will look into the following topics with an example for each.
- Scope
- Global Scope
- Local Scope
- Function scope
- Lexical/static scope
- Closures
- ‘this’ object and scope
- Changing scope with call()
- Public and private scope
Scope
Scope means the current context of your code. A scope can be global or local. By understanding scope you will understand where variables and functions/objects are accessible , and you will be able to change the scope of your code. It enables us to write more maintainable code as well as to debug much faster.
Global Scope
We are in global scope when we start writing any piece of JavaScript. For example when we declare a variable then the variable is globally defined, which means it is accessible from anywhere in the script.
Example
var app = 'Demo';
console.log(app);
function funcA() {
console.log("inside a function: ", app);
};
funcA();
Explanation
In the above example, the variable
app
is defined at global scope. As you can see the variable is called from global scope and also from inside a function. Because the variable is defined a global level it is accessible from anywhere within the script.There is typically only one global scope.
The console output is
Local Scope
When you define a function in the global scope (top level) or within another function (local scope) then the function has its own local scope. The local scope of outer function is accessible to the inner function.
Example 1
1 var globalVar = "Global Scope";
2 var funcG = function () {
3 // Local scope for funcG()
4 var funcName = 'funcG';
5 console.log(funcName);
6 //Local scope can access the global scope
7 console.log(globalVar);
8 };
9 funcG();
10
11 //Global scope cannot access the local scope.
12 console.log(funcName);
Explanation
The following is the output from the above example 1 where as you can see the variable
funcName
is undefined in the last line console.log(funcName);
because it is defined in local scope inside a function where as the global variable globalVar
is accessible in console.log(globalVar);
inside local scope.
Example 2
function funcOuter() {
var outerVar = 'local variable funcOuter();';
function funcInner(){
var innerVar = 'local variable funcInner()';
console.log('inside funcInner : ', innerVar);
console.log('inside funcInner : ', outerVar);
}
console.log(outerVar);
console.log(innerVar);
}
Explanation
The following is the output for example 2 where the
innerVar
is undefined because it is defined insidefuncInner()
function and inside local scope which is not accessible to outside function.
At the same time the variable
outerVa
r defined in function funcOuter
is accessible inside funcInner()
because outer local scope is accessible in inner local scope.Function Scope
A scope is not created for each statements but for each functions. When a
FOR
or WHILE
statement is used, a new scope is not created. A function means a new scope that’s the thumb rule.
Example
function funcLoop() {
for (i = 0; i < 5; i++) {
console.log('inside loop :', i);
}
console.log('outside loop :', i);
}
funcLoop();
Explanation
The following is the output for the above code and as you can see the variable
i
defined inside the FOR
statement and is accessible from outside the loop too which proves that there is no scope inside the statement.Lexical/Static Scope
When you define a function within another function, the inner function has access to the scope in outer function. This is called Lexical or Static scope.
Example 1
var parentFunc = function () {
var param1 = 'parameter 1';
var childFunc = function () {
console.log('The parameter is :', param1);
}
childFunc();
console.log(param1);
}
parentFunc();
Explanation
The output for the above code is shown below. As you can see, the
childFunc()
is able to access the varparam1
that is defined in outer function.
The lexical scope works from outer most function to inner most function. It does not work backwards.
Example 2
function outerMost() { console.log(innerMost); function outer() { console.log(innerMost); function inner() { console.log(innerMost); function innerMost() { var innerMost = 'inner most'; } } } } outerMost();
Explanation
In the example 2 , the var
innerMost
is defined in innerMost()
function and it is not accessible to outer functions. The output is shown below.Closures
A closure is an object that returns two things: A function and an environment with it.
Closures tie in very closely with Lexical scope. We can return things inside our scope so that they are available in the parent scope.
We can demonstrate it with the following example.
Example
var echo = function (shout) { var txt = 'Echoing back : ' + shout; return function () { console.log(txt); } } var f1 = echo('Hello'); f1();
Explanation
The function
echo()
in the above example, returns a function that displays a text using the vartxt
. Thetxt
is defined within the echo()
function. However , as you can see from the following output , the echo()
function is return with the environment (scope) of var txt within it.'this’ object and scope
The keyword
‘this’
is a very important part of JavaScript programming , and understanding how value of‘this’
is assigned in different places in code and the way how the functions are called is absolutely paramount to write a bug-free code.
The value of
‘this’
at the very top level is the window object. However, the value of ‘this’
is bound to have different values by invoking functions differently. We can easily demonstrate using an example.
Example
var myFunc = function () {
console.log(this);
};
myFunc();
var myObj = {};
myObj.method1 = function () {
console.log(this);
};
myObj.method1();
<p class='scope'>Click</p>
Explanation
The first function
myFunc()
returns the object window as 'this'
which is shown below in the output.
The second function
myObj.method1()
returns the object myObj
for 'this'
because the function method1()
is part of the object myObj
. The output is shown below.
Thirdly, the function
display()
returns the <p>
element for 'this'
because the click()
function is called on the element <p>.
The output is shown below.
Changing the scope with call()
The scope of
'this'
can be changed using the function call()
in our code.
Let’s consider the following example
<ul>
<li>Red</li>
In the
‘for’
loop the developer is trying to log the list items values. But the actual values that are being logged in console are window object because ‘this’
refers to window object in the scope. The console output and the browser output are given below.
Of course we can get the value of list item using array index as below.
console.log(listItems[l]);
In order to demonstrate how a scope can be changed by changing the context, we are going to use the
call ()
method in the above code. The same code is rewritten using call ()
method and we can see how it changes the context of the code and its scope.var listItems = document.querySelectorAll('ul li');
for (var l = 0; l < listItems.length; l++) {
(function () {
console.log(this);
}).call(listItems[l]);
}
The output of the above is shown below. This time , as you can see the <li> elements are returns for 'this' object. This way the function
call()
is very powerful in certain situations.Public and Private Scope
Inherently , there is no public and private scope in Javascript unlike in other languages but we can emulate them using module pattern.
Lets see the following example
var myModule = (function () {
var _myPrivateMethod = function () {
console.log('_myPrivateMethod()');
}
return {
methodOne: function () {
console.log('methodOne()');
}
};
})();
myModule.methodOne();
myModule._myPrivateMethod();
In the above code the line
myModule._myPrivateMethod();
would fail because the function_myPrivateMethod()
is accessible because it is a private method to myModule
.
However the line of code
myModule.methodOne();
would succeed though because the method made public with thereturn
statement.Points of Interest
It is worth having a read on
bind()
and apply()
that are similar to call()
with slight variation. It is also worth reading more in detail aboutmodule
pattern because it is key to Angular JS
.
No comments:
Post a Comment