# Tricky Parts of JavaScript II — Scoping, Hoisting & `const` Keyword

This is the sequel to a previous article which we tackled the intricacies of the various data types and their behavior with the assignment operator. We also uncovered the tricky aspects of equality operators. If you missed it, you can catch up [here](https://theody.hashnode.dev/tricky-parts-of-javascript-i-data-types-vs-assignment-equality-operators).

**Now, let's jump into the next part of our JavaScript journey and explore some of the more tricky aspects!**

## **1\. Scoping**

In JavaScript, **scoping determines where in your code a particular variable, function, or identifier is accessible.** There are two fundamental types of scope:

* **Global Scope:** Variables declared outside any function or block have global scope, meaning they are accessible throughout the entire program.
    
* **Local Scope:** Variables declared inside a function or block have local scope, meaning they are only accessible within that function or block. This can be subcategorized into **Function Scope** and **Block Scope**.
    

**Note:** There is also the concept of lexical or static scoping in JS which is beyond the scope of this article.

**Now let’s explore how local scoping can be tricky with the** `var`**,** `const` **and** `let` **keywords used for variable declarations:**

```javascript
// Example 1
function scopeExampleOne() {
    if (true) {
        var x = 10;
    }
    console.log(x); // 10
}
scopeExampleOne();

// Example 2
function scopeExampleTwo() {
    if (true) {
        let y = 20;
    }
    console.log(y); // ReferenceError: y is not defined
}
scopeExampleTwo();


// Example 3
function scopeExampleThree() {
    if (true) {
        const z = 20;
    }
    console.log(z); // ReferenceError: z is not defined
}
scopeExampleThree();
```

Let’s delve into each example to understand the behaviors seen above.

### **Example 1 — var (Function Scope)**

* In this example, the variable `x` is declared using `var`, which is **function-scoped.**
    
* This means that the variable is accessible throughout the entire function, even though it was declared inside the `if` block.
    
* Hence, the `console.log(x)` statement outside the `if` block successfully logs the value of `x` due to this scoping behavior.
    

### **Example 2 — let (Block Scope)**

* In this case, the variable `y` is declared with `let`, which has **block scope.**
    
* Variables with block scope are limited to the block (in this case, the `if` block) where they are defined.
    
* Attempting to access `y` outside the block results in a `ReferenceError` because it is not defined in that scope.
    

### **Example 3 — const (Block Scope)**

* Similar to Example 2, the variable `z` is declared using `const`, which also has **block scope.**
    
* The attempt to log `z` outside the `if` block results in a `ReferenceError` because `z` is not defined in that scope.
    

## **2\. Hoisting**

**Hoisting** is a behavior in JavaScript where **variable and function declarations are moved to the top of their containing scope during the compilation phase.** This means that you can use variables and functions in your code before they are declared.

There are two main types of hoisting in JavaScript: **Variable Hoisting** and **Function Hoisting.**

Now, let’s examine some tricky aspects of hoisting with the code snippet below:

```javascript
// Example 1
console.log(a); // undefined, not error
var a = 10;

// Example 2 
console.log(b); // ReferenceError: b is not defined
let b = 20;

// Example 3 
console.log(c); // ReferenceError: c is not defined
const b = 30;

// Example 4
foo(); // "Hello, world!"
function foo() {
  console.log("Hello, world!");
}
```

### **Example 1 — Hoisting with var**

* In JavaScript, variables declared with `var` are hoisted to the top of their scope during compilation. This allows you to use the variable before its declaration.
    
* **Note that only the declaration**, not the initialization (assignment), is hoisted, and the variable is initialized with `undefined` during this process.
    

**In this example,**

* `a` is hoisted and the variable declaration is effectively moved to the top of the scope. However, the assignment (`var a = 10`) hasn't happened yet at this point.
    
* The `console.log(a)` statement prints `undefined` because, during the initial pass, the variable `a` is recognized but not assigned a value yet.
    

### **Examples 2 & 3 — Hoisting with let and const**

* Variables declared with `let` and `const` are hoisted to the top of their containing block but are not initialized during this phase.
    
* Instead of being initialized with `undefined` like variables declared with `var`, they stay in an uninitialized state.
    
* This creates the [**temporal dead zone**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz), which refers to the period between the start of the current scope and the point where the variable is declared. During this zone, attempting to access or read the value of the variable leads to a `ReferenceError`.
    

**In these examples,**

* The lines `console.log(b)` and `console.log(c)`encounters an error because the variables `b` and `c` have not been initialized at this point. This is a temporal dead zone for the variables `b` and `c`.
    

### **Example 4 — Function Hoisting**

* Function declarations are hoisted to the top of their scope during the compilation phase, similar to variable hoisting.
    
* Unlike variables, both the declaration and the function’s body are hoisted to the top of the scope.
    

**In this example,**

* When `foo()` is called, it successfully logs "Hello, world!" to the console. This is possible because the function declaration is hoisted, and the entire function is available for execution.
    

### **Note: Hoisting of Function Expressions**

When using a function expression assigned to a variable, the declaration is hoisted, but the assignment is not. This behavior contrasts with function declarations, where the entire function is hoisted.

**Let’s look at an example:**

```javascript
//Function Expressions with `var`
foo(); // TypeError: foo is not a function
var foo = function() {
  console.log("Hello, world!");
};

bar(); // TypeError: bar is not a function
var bar = () => {
  console.log("Hello, world!"); // ES6 Arrow Function
};


//Function Expressions with `let`
foo(); // ReferenceError: Cannot access 'foo' before initialization
let  foo = function() {
  console.log("Hello, world!");
};

bar(); // ReferenceError: Cannot access 'bar' before initialization
let  bar = () => {
  console.log("Hello, world!"); // ES6 Arrow Function
};


//Function Expressions with const
foo(); // ReferenceError: Cannot access 'foo' before initialization"
const foo = function() {
  console.log("Hello, world!");
};

bar(); // ReferenceError: Cannot access 'bar' before initialization"
const bar = () => {
  console.log("Hello, world!"); // ES6 Arrow Function
};
```

**In this code snippet,**

* The variables are hoisted to the top of their scopes during the compilation phase, similar to variable declarations. However, only the declaration is hoisted, not the assignment.
    
* For `var` declarations, attempting to call `foo()` and `bar()` before the assignment results in a `TypeError` because `foo` and `bar()` are declared and initialized as `undefined`, but not yet assigned a function.
    
* Both `let` and `const` declarations are hoisted, but they are in the "[**temporal dead zone**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz)" until the line of code where they are assigned. Attempting to call `foo()` and `bar()` before the assignment results in a `ReferenceError` because though they are declared, they remain uninitialized.
    

## **3\.** `const` **Keyword**

The use of “**const**” in JavaScript declares a variable that cannot be reassigned after its initialization.

Let’s consider the code snippet below to examine some tricky aspects of the “const” keyword:

```javascript
// Example 1
const gravity = 9.8;
gravity = 9.81; // TypeError: Assignment to constant variable.

// Example 2
const planet = "Earth";
planet = "Mars"; // TypeError: Assignment to constant variable.

// Example 3
const colors = ["red", "green", "blue"];
colors.push("yellow");
console.log(colors); // ["red", "green", "blue", "yellow"]

//Example 4
const person = { name: "Fred", age: 25 };
person.age = 26;
console.log(person); // { name: "Fred", age: 26 }
```

Before delving into the examples provided, a brief revisit to my [previous article](https://theody.hashnode.dev/tricky-parts-of-javascript-i-data-types-vs-assignment-equality-operators) in this series would serve as a helpful reference.

This will remind us that the variables `gravity` and `planet` in examples 1 and 2 respectively belong to the category of **primitive data types**.

Similarly, the variables `colors` and `person` in examples 3 and 4 fall under the **reference data types category.**

**Now, let’s examine each category in the code snippet above:**

### **Example 1 & 2 — Immutability for Primitive Values**

* In these examples, the attempt to reassign the constant variables `gravity` and `planet` resulted in a `TypeError`.
    
* This error occurs because the `const` declaration ensures that the variable remains constant and cannot be reassigned after initialization.
    

### **Example 3 & 4 — Immutability for Reference Types**

* In these examples, despite the use of the `const` keyword, the content of arrays or objects can be modified.
    
* This is because, for reference data types, `const` doesn't impose immutability on the contents; it merely prevents reassignment of the variable.
    
* As such, attempting to reassign the entire array or object will result in an error:
    

```javascript
// Example 3
const colors = ["red", "green", "blue"];
colors = ["orange", "purple", "pink"]; // TypeError: Assignment to a constant variable.

// Example 4
const person = { name: "Alice", age: 25 };
person = { name: "Bob", age: 30 }; // TypeError: Assignment to a constant variable.
```

**In these cases,**

* The attempt to reassign a new value to the entire variable `colors` or `person` throws an error.
    
* `const` ensures that the variable itself cannot be reassigned but allows for modification of the contents.
    

## **In Conclusion,**

We’ve explored some tricky parts of JavaScript, from **scoping** and **hoisting** to the unique traits of `const` variables—tools that’ll help you optimize your code.

Stay tuned for more insights!

[  
](https://medium.com/@theody85?source=post_page---post_author_info--338a106e989f--------------------------------)
