1.Javascript Hoisting ?
Why you can access certain variables in some parts of your code and Why you can’t in a verse now.
Hoisting is in the end just a core mechanism built into Javascript or into Javascript engines that run your code that in the end and ables javascript to be aware of certain variables and functions before the code execution reaches the function or variable declaration.
Because of the hoisting mechanism javascript essentially goes over your entire file before it starts executing it and you could say it memorizes all those function declarations. Thats why you’re then able to call such a function when the code execution begins before the code execution reached those lines down they are.
Example 1 :
let result = 1;
console.log(addOne(3));
console.log(result); //output : 4
function addOne(numToAdd){
result = result + numToAdd;
return result
}
Note : Hoisted not working in the below scenario (Function Expression)
if instead we would have created a addOne constant and stored an anonymous function in there and hence we would have used this function expression syntax, the addOne variable would kind of be hoisted but you wouldn’t be able to call it
Example 2 :
let result = 1;
console.log(addOne(3))
console.log(result);
const addOne = function (numToAdd) {
result = result + numToAdd;
return result;
}
So hoisting is really most important to be aware of or to utilize if you are using this syntax(function declaration) because This allows you to move functions to the end of your file which can help you organize your code
Like variables, functions are also hoisted in JavaScript. However, how the hoisting will be done depends on the way a function is declared.
JavaScript allows developers to define a function in two ways-
- function declaration
- function expression
Let’s consider the following example.
function test()
{
foo();
bar();
// Function defiened
// using function declaration
function foo()
{
console.log('foo');
}
// Function defined
// using function expression
var bar = function() {
console.log('bar');
}
}
test();
//Output :
foo
TypeError: bar is not a function
The JavaScript interpreter interprets the above code as follows.
function test()
{
function foo() {}
var bar;
foo();
bar();
// Function defiened
// using function declaration
function foo()
{
console.log('foo');
}
// Function defined
// using function expression
bar = function() {
console.log('bar');
}
}
test();
Since function foo() is defined using function declaration, JavaScript interpreter moves its declaration to the top of its container scope, i.e., the body of test(), leaving its definition part behind. The definition is dynamically assigned when foo() is invoked. This leads to the execution of the body of foo() function when the function is called. On the other hand, defining a function using function expression is nothing other than variable initialization where the function is treated as the value to be assigned to the variable. Therefore it follows the same hoisting rule which is applicable to variable declarations. Here the declaration of the variable ‘bar’ is moved to the top of its container scope while the place of assigning the function to it remains unchanged. JavaScript cannot interpret a variable as a function until it is assigned a value which is actually a function. This is why trying to execute the statement bar(); before ‘bar’ is defined results in a TypeError
Hoisting is JavaScript’s default behavior of moving declarations to the top.
1. JavaScript Declarations are Hoisted
In JavaScript, a variable can be declared after it has been used.
In other words; a variable can be used before it has been declared.
Example 1
x = 5; // Assign 5 to x
elem = document.getElementById("demo"); // Find an element
elem.innerHTML = x; // Display x in the element
var x; // Declare x
Hoisting is JavaScript’s default behavior of moving all declarations to the top of the current scope (to the top of the current script or the current function).
2. JavaScript Initializations are Not Hoisted
JavaScript only hoists declarations, not initializations.
Example 2
var x = 5; // Initialize x
elem = document.getElementById("demo"); // Find an element
elem.innerHTML = x + " " + y; // Display x and y
var y = 7; // Initialize y
Does it make sense that y is undefined in the last example?
This is because only the declaration (var y), not the initialization (=7) is hoisted to the top.
Because of hoisting, y has been declared before it is used, but because initializations are not hoisted, the value of y is undefined.
Scope is a region of the program where a variable can be accessed. In other words, scope determines the accessibility/visibility of a variable. C-family, has block-level-scope.Whenever the control enters into a block, such as if-block or a loop (e.g., for, while or do-while), the language allows new variables to be declared that can be visible within that block only. Here the variables declared in the inner scope do not affect the value of the variables declared in the outer scope. But this is not the case in JavaScript.
Example 1 :
var x = 10;
console.log(x);
if (true) {
var x = 20;
console.log(x);
}
console.log(x);
//Output :
10
20
20
This is because JavaScript does not have block-level-scope. It has function-level-scope.That means blocks such as if-statements and loops do not create new scopes in JavaScript. Rather a new scope is created only when a function is defined.
Example 2 : Luckily, JavaScript allows function definitions to go inside any block. For example, we can write the above code by implementing an IIFE (Immediately Invoked Function Expression) inside the if-block that will result in a different output.
Example 2 : IIFE
var x = 10;
console.log(x);
if (true) {
(function() {
var x = 20;
console.log(x);
})();
}
console.log(x);
//Output:
10
20
10
ES6
Hence, to keep the things simple ES6 has introduced two new keywords- ‘let’ and ‘const’, to declare block-scoped variables. When a variable is declared using ‘const’ or ‘let’, it is visible only inside the particular block in which it is declared. For Example 3 :
var x = 10;
console.log(x);
function test()
{
var x = 20;
console.log(x);
if (x > 10) {
let x = 30;
console.log(x);
}
console.log(x);
}
test();
console.log(x);
//Output :
10
20
30
20
10
The keyword ‘const’ also operates in a similar manner. The only difference between ‘let’ and ‘const’ is– const is a signal that the identifier won’t be reassigned (The reason that the word ‘identifier’ is preferred here over ‘variable’ is since ‘const’ is used to declare the identifier, it is no longer a variable. It is a constant.), whereas let is a signal that the variable may be reassigned.
Example 4 : Reference Error :
var x = 10;
function test()
{
if (x > 20) {
var x = 50;
}
console.log(x);
}
test();
Example 4 : Since JavaScript (especially the var keyword) has function-level-scope, the variable ‘x’ declared inside the if-block is visible throughout the function test(). So when the console.log() statement executes, it tries to print the value of the inner ‘x’ rather than the value of the one declared outside the function definition. Now the question is if this code snippet prints the value of the inner ‘x’ then why is it printing ‘undefined’? Here the inner ‘x’ is both declared and defined inside the if-block which is evaluated to false and in JavaScript an attempt to access a variable before its declaration results in a ReferenceError.
Then how come the variable is even getting declared inside the function allowing it to be executed without any error?
Hoisting in Javascript
Hoisting is JavaScript’s default behavior of moving declarations to the top of their containing scope. When a JavaScript code is interpreted, the interpreter invisibly moves (hoist) all the variable and function declarations to the top of the scope they are declared in. However, the location of their definition/initialization/instantiation remains unaffected.
For example, the above code (example 4 ) snippet will be interpreted as the following before execution.
var x;
x = 10;
function test()
{
var x;
if (x > 20) {
x = 50;
}
console.log(x);
}
test();
In this case, since no value is assigned to the variable ‘x’ declared at the top of the test() function, JavaScript automatically assigns the value ‘undefined’ to it. Since the if-condition is evaluated to false, the ‘console.log()’ statement prints ‘undefined’ on the console.
Note : Now the important point here is, this variable hoisting mechanism works only for variables declared using var keyword. It does not work for variables or identifiers declared using let and const keywords respectively. Let’s consider the following example.
Identifiers declared using let or const are not at all hoisted. This makes them inaccessible before their declaration in the original source code.
function test()
{
if (false) {
let x = 50;
}
console.log(x);
console.log(y);
let y = 100;
console.log(y);
}
test();
//Output :
ReferenceError: x is not defined
3. Declare Your Variables At the Top !
Hoisting is (to many developers) an unknown or overlooked behavior of JavaScript.
If a developer doesn’t understand hoisting, programs may contain bugs (errors).
To avoid bugs, always declare all variables at the beginning of every scope.
Note : JavaScript in strict mode does not allow variables to be used if they are not declared.
Study “use strict” in the next chapter.