Hey there peeps! I am back with another article... this time it is about one of the core concept of JS : Scope. The idea here is to explain how scoping works and what key notes you can take away from this important concept. So without further ado, lets get started with it...
π What is Scope ?
- The scope is the current context of execution in which values and expressions are "visible" or can be referenced. If a variable or expression is not in the current scope, it will not be available for use. Scopes can also be layered in a hierarchy, so that child scopes have access to parent scopes, but not vice versa.
- In other words, scope refers to the boundary in which a variable or expression is relevant or holds certain meaning.
For example, consider the below code :
let nam = 'Manik';
const sal = 50000;
function display(){
console.log(`Name is ${nam}`);
console.log(`Salary is ${sal}`);
let sample = 90;
console.log(sample);
}
display();
console.log(sample);
Now consider the error this code generates upon execution :
In this program, the area/boundary in which the variable 'sample' operates or is relevant lies within the function 'display'. Hence, since the scope of the variable is only limited to the function, accessing it out of scope gives an error message.
β¨ Types of Scope
- Global scope : The default scope for all code running in script mode.
- Module scope : The scope for code running in module mode.
- Function scope : The scope created with a function.
- Block scope : The scope created with a pair of curly braces (a block).
Now let us understand these one by one through examples as scope as a concept is best understood by actual programs that work.
πΊ Global scope
let global_IP = '192.168.11.34';
console.log(global_IP);
- In the above code, the variable 'global_IP' is defined outside any function, block, or module scope and hence, has global scope.
- This means, it can be accessed from anywhere in the program.
π Module scope
// In country.js, exporting to local.js
export { countries, states, print };
- In the above code, suppose we need a function or object available which is present in a different module or js file available to other modules, we use the keyword 'export'.
- Importing makes a function or object, from other modules, available to the current module.
π΅ Function scope
function sayHi(){
let word = 'Hi there!';
console.log(word);
}
sayHi();
console.log(word);
- In the above code, the variable 'word' is only available within the function 'sayHi()' so trying to access it outside the function would throw an error.
- You may use the variable 'word' by calling the function 'sayHi()'.
π΄ββοΈ Block scope
let x = 10;
{
let x = 20;
console.log(x);
}
console.log(x);
- In the above code, the variable x defined within the curly braces {} has a boundary only within that curly braces, hence printing to console gives varied results when used within and outside the scope.
π±βπ Lexical Scope
- Lexical scope is the ability of the inner function to access the outer scope in which it is defined.
- You may call it as a hierarchical access given to inner scope from outer scope.
let outer = 10;
function sum() {
let inner = 20;
function innerSum(n) {
let innerMore = 30;
return outer+inner+innerMore+n;
}
return innerSum;
}
let summer = sum();
console.log(summer(100));
- In the above code, variable 'outer' is present in global scope hence, can be accessed by 'sum()' and 'innerSum()' both as all 3 scopes are linked hierarchically.
- The variable 'inner' has a function-level scope within 'sum()' hence, can be accessed by 'innerSum()'.
- The variable 'innerMore' is within 'innerSum()' hence, can only be accessed within this function and not anywhere outside.
βοΈ Scope Chaining
- It is the process in which, JavaScript engine searches for the value of the variables in the scope of the functions. However, the search is in a lexical manner. First of all the engine looks out in the current scope of the current function. If not found, it finds it in the parent function.
let x = 100;
let y = 200;
function print(){
console.log(y); //Error : Trying to access before initialization ; Note that y is also present in global scope
let y = 300;
console.log(y);
function innerPrint(){
console.log(x);
let x = 300;
console.log(x);
}
return innerPrint;
}
let myPrint = print();
myPrint();
- The above code gives an error, but why should it ? 'x' and 'y' both are defined in global as well as function level scope.
- Due to scope chaining, first the current scope is checked and here it finds 'y' initialized so it does not look for the globally scoped 'y'.
- It wants the currently scoped 'y' to be used but as it is initialized afterwards it shows a Reference Error.
Similarly, if we comment the error line having 'y', it will show error on 'x':
let x = 100;
let y = 200;
function print(){
// console.log(y);
let y = 300;
console.log(y);
function innerPrint(){
console.log(x);
let x = 300;
console.log(x);
}
return innerPrint;
}
let myPrint = print();
myPrint();
Due to this, if both conflicting 'x' and 'y' are commented out, the due to scope chaining, the global 'x' and 'y' are printed :
let x = 100;
let y = 200;
function print(){
// console.log(y);
// let y = 300;
console.log(y);
function innerPrint(){
// console.log(x);
// let x = 300;
console.log(x);
}
return innerPrint;
}
let myPrint = print();
myPrint();