What is scoping and hoisting in JavaScript? Levels of scoping in JS

What is scoping and hoisting in JavaScript? Levels of scoping in JS

In the previous blog on JavaScript Functions, you learned about the basics of what functions help with. Functions help you perform the same task again and again for a given input.

Let’s learn three major concepts of scoping, hoisting, and arrow functions in JavaScript in this blog. Buckle up! It is going to be lengthy but interesting.

Scoping in JavaScript

There are multiple ways to do scoping in JavaScript. Let’s look at a few ways:

Scoping with let in JavaScript

You have learned about the const and let variables in JS already. To refresh your memory, check out this blog. What would happen if you define a const or let variable inside a function?

function greeting(name) {
    let greet = "Hello"
    console.log(`${greet}! I am ${name}`)
}

greeting("Rishabh")Code language: JavaScript (javascript)

The output of this code will be:

Hello! I am RishabhCode language: plaintext (plaintext)

What do you think will be the output of the code when I do this:

function greeting(name) {
    let greet = "Hello"
    console.log(`${greet}! I am ${name}`)
}

greeting("Rishabh")
console.log(greet)Code language: JavaScript (javascript)

Let’s check it out:

Scoping with let
Click here to get the code

It gives a ReferenceError. What that means is that JavaScript doesn’t know where the greet variable has been defined and in fact, the error message also reads that greet is not defined. But it is clearly not true! Or is it?

Let me walk you through what is happening:

Scoping with let
Click here to get the code

As commented, the function starts at line 1 and ends at line 4. Whatever you place inside the {} of a function is said to come under a function’s scope. As soon as this scope ends at line 4, JavaScript will no longer be able to access any variables defined inside that function, in this case greet.

The only exception to this is that the function is actually returning the value you are trying to access. So the following would work:

function greeting(name) {                  // Function starts
    let greet = "Hello"                    // greet variable is defined
    console.log(`${greet}! I am ${name}`)  // greet variable and name parameter are printed
    return greet
}                                          // Function is closed

// Calling the function
let greet = greeting("Rishabh")

// Printing greet
console.log(greet)
Code language: JavaScript (javascript)

Here you are explicitly storing the value the function is returning in a variable outside the scope of the function. Read that again!

Scoping with const in JavaScript

What if I replace let with const?

Scoping with const
Click here to get the code

The output still remains the same! You get the same error message. So even variables defined with const are not available outside the function scope.

This property of let and const not being available outside the function scope is called as blocked scoping. The same is applicable for the variables defined inside an if block. You can watch this 1-minute video for a better understanding:

Scoping with const

Any variable outside the if block or a function block is said to be globally scoped.

Global Scope
Click here to get the code

A globally scoped variable like greetOne can be accessed from anywhere in the code.

JavaScript will first check if a variable is available in the local scope. A local scope is all the code inside the curly braces {} be it a function or an if-else block. If it is available, it will use the value from the local scope. Otherwise, it will move to the global scope and see if the value is available. If it is not available even in the global scope, it will give a ReferenceError.

Experiment with the following code to cement your understanding of scoping with const and let:

// Global Scope
let msg = "I'm in the GLOBAL scope"
console.log(msg)

// Block scoped
if (true) {
    let msg = "I'm in the IF BLOCK"
    console.log(msg)
}

// Function scope
function foo() {
    let msg = "I'm in the FUNCTION BLOCK"
    console.log(msg)
}
foo()
Code language: JavaScript (javascript)

Now that you fully understand scoping, let us look at hoisting in JavaScript!

Hoisting in JavaScript

Do you think the following code will work?

greet()

function greet() {
    console.log("Hello Rishabh!")
}
Code language: JavaScript (javascript)

See carefully, that the function greet has been called before its definition. Check the output below:

Hoisting in JavaScript
Click here to get the code

It actually prints Hello Rishabh! This behaviour of JavaScript is referred to as Function Hoisting!

When the JavaScript interpreter runs the code, it goes through all the function definitions declared with the following syntax:

function functionName() {
    // Code to run
}Code language: JavaScript (javascript)

And it moves this declaration to the global scope!

You can think of JavaScript first declaring the function in line 1, and then calling greet().

Hoisting Functions
Click here to get the code

This means that usually, the sequence in which the code is written in a file does not matter that much. What matters is the order of its execution!

But, what if you want to avoid this behaviour of JavaScript? That’s where arrow functions come in!

Arrow Functions in JavaScript

function multiply(x, y) {
    return x * y
}Code language: JavaScript (javascript)

Let us see the above function multiply as an arrow function:

const multiply = (x, y) => {
    return x * y
}Code language: JavaScript (javascript)

It is just a new way of writing functions! You replace the function keyword with the const or let and then add an equal to symbol and an arrow to the existing code! That is about it.

It surely can’t just be syntactical, otherwise, what’s the use? Well for starters, you can write the above function in a more compact manner!

const multiply = (x, y) => x * yCode language: JavaScript (javascript)

This will work exactly the same. I have removed the curly braces, an extra line, and the return statement from the code. And if you have only one parameter and do a simple operation like add a 1 to it or print it to the console, then you can also omit the parenthesis ():

let printNum = x => console.log(x)
const addOne = x => x + 1

printNum(5)
addOne(5)Code language: JavaScript (javascript)

printNum and addOne are functions.

Therefore, one advantage is that it reduces the verbosity of code.

Let us pick up the code for calculating the average score from the previous blog.

function average(marks) {
    let sumMarks = 0
    for (let i = 0; i < marks.length; i++) {
        sumMarks += marks[i]
    }

    let average = sumMarks / marks.length
    return average
}
Code language: JavaScript (javascript)

When we convert this to an arrow function, it will look something like this:

const average = marks => {
    let sumMarks = 0
    for (let i = 0; i < marks.length; i++) {
        sumMarks += marks[i]
    }

    let average = sumMarks / marks.length
    return average
}Code language: JavaScript (javascript)

As you can see, there was almost no reduction in verbosity in this case. So then the question stands, beyond small use cases, why use arrow functions at all?

Try this out:

console.log(multiply(5, 4))

let multiply = (x, y) => {
    return x * y
}Code language: JavaScript (javascript)

In the previous section, we had seen that a function defined with function keyword ran for a case like the one above. However, this won’t run!

Hoisting Arrow Functions
Click here to get the code

That’s because a function created using the arrow syntax is not hoisted to its higher scope (in this case, the global scope). And so, JavaScript cannot know that a function multiply has been defined, giving us yet another ReferenceError.

Thus, using arrow functions will bypass Function Hoisting in JavaScript.

Conclusion

We covered a lot of bases on scoping, hoisting, and arrow functions in JavaScript.

For scoping, you learned about the local and global scopes and the function and block scopes. In hoisting you saw, how the order of code written matters less. Arrow functions helped you reduce verbosity in some cases and bypass hoisting in others!

In the next blog, you will learn more about adding methods to the Object Data Type and the this keyword in JavaScript. See you then!

Sharing is caring

Did you like what Rishabh Mittal wrote? Thank them for their work by sharing it on social media.

0/10000

No comments so far