Skip to main content

Javascript Interview Questions ( Var, Let and Const ) - Hoisting, Scoping, Shadowing

 

1. var, let, and const Declarations :

  • var: Introduced in ES5, var is function-scoped and can be redeclared within the same scope. Variables declared with var are hoisted to the top of their scope but are initialized with undefined.

  • let: Introduced in ES6, let is block-scoped and cannot be redeclared within the same scope. Variables declared with let are hoisted but are not initialized, leading to a "temporal dead zone" until the declaration is encountered.

  • const: Also introduced in ES6, const is block-scoped and must be initialized at the time of declaration. Like let, const declarations are hoisted but not initialized, resulting in a temporal dead zone. Variables declared with const cannot be reassigned.

2. Hoisting

Hoisting refers to the JavaScript engine's behavior of moving variable and function declarations to the top of their scope before code execution.

  • var Hoisting: Variables declared with var are hoisted and initialized with undefined. This means you can reference them before their declaration without causing an error, though the value will be undefined.

        Example: 
        console.log(a); // Outputs: undefined
            var a = 5;

  • let and const Hoisting: Variables declared with let and const are hoisted but not initialized.        Accessing them before their declaration results in a ReferenceError due to the temporal dead zone.
            console.log(b); // Throws ReferenceError
            let b = 10;

3. Scoping?

Scoping defines where variables are accessible in your code. JavaScript has three main types of scope:

  • Global Scope
  • Function Scope (Local Scope)
  • Block Scope
  1. Global Scope

        A variable declared outside any function or block is globally scoped. It can be accessed anywhere            in the script.

var globalVar = "I am global";

function test() {

    console.log(globalVar); // Accessible inside function

}

test();

console.log(globalVar); // Accessible anywhere

output

-----

I am global

I am global


2. Function Scope (Var) : Variables declared with var are confined to the function in which they are declared. They are not accessible outside that function.

Example: 

function myFunction() {

    var localVar = "I exist only inside this function";

    console.log(localVar); // Works

}

myFunction();

console.log(localVar); // Error: localVar is not defined

output:
I exist only inside this function
ReferenceError: localVar is not defined


3.  Block Scope: Variables declared with let and const are confined to the block and only accessible             within that block. 

example:
{
    let blockVar = "I am inside a block";
    const blockConst = "Me too!";
    console.log(blockVar); // Works
    console.log(blockConst); // Works
}

console.log(blockVar); // Error: blockVar is not defined
console.log(blockConst); // Error: blockConst is not defined

output
------
I am inside a block
Me too!
ReferenceError: blockVar is not defined
ReferenceError: blockConst is not defined


4. Shadowing:

Shadowing happens when a variable in an inner scope has the same name as a variable in an outer scope. The inner variable overrides (shadows) the outer variable within its scope.

Example (Shadowing with let and const):

Example:

let x = "Outer Scope"; { let x = "Inner Scope"; // Shadows the outer 'x' console.log(x); // Inner Scope } console.log(x); // Outer Scope

Output:

Inner Scope Outer Scope

📌 Explanation: The inner let x = "Inner Scope"; only exists inside the block, and once outside, the original x is still accessible.


 1. Function Scope vs. Block Scope Shadowing

Example (var vs. let shadowing):

var y = "Global Scope"; function test() { var y = "Function Scope"; // Shadows global 'y' inside function console.log(y); } test(); console.log(y); // Still Global Scope

Output:

Function Scope Global Scope

📌 Explanation:

  • var y inside the function shadows the global y only inside the function.
  • Outside the function, y is still "Global Scope".

2. Illegal Shadowing (with var and let)

In some cases, shadowing can cause errors, especially when var is used inside a block where let is already declared.

Example (Illegal Shadowing):

let z = "Outer"; { var z = "Inner"; // ❌ Error: Identifier 'z' has already been declared console.log(z); }

Output:

SyntaxError: Identifier 'z' has already been declared

📌 Why?

  • You cannot declare a var variable if there’s already a let variable with the same name in the outer scope.
  • But the reverse (shadowing var with let) is allowed.

Fixed Version:

var z = "Outer"; { let z = "Inner"; // ✅ Allowed console.log(z); // Inner } console.log(z); // Outer

3. Function Scope vs. Block Scope Shadowing (Real Use Case)

Let’s say we want to log a user’s name, but mistakenly shadow a variable inside a loop.

Example (Bug caused by Shadowing):

let username = "Alice"; function printUser() { for (let i = 0; i < 1; i++) { let username = "Bob"; // Shadows the global username console.log(username); // Bob } console.log(username); // Alice (Global) } printUser();

Output:

Bob Alice

📌 Why?

  • The loop creates a new username variable inside the block, which only exists there.
  • Outside the block, username still refers to "Alice".





Comments

Popular posts from this blog

Closure + setTimeout Interview Questions

 A closure in JavaScript is a function that retains access to its lexical scope, even when the function is executed outside that scope. This means that a closure can remember and access variables from its outer function even after that function has finished executing. citeturn0search0 Key Characteristics of Closures: Lexical Scoping: Functions in JavaScript form closures by capturing variables from their surrounding lexical environment. This allows inner functions to access variables defined in their outer functions. Persistent State: Closures enable functions to maintain a persistent state. Since the inner function has access to the outer function's variables, it can remember and modify these variables across multiple invocations. Practical Applications of Closures: Data Encapsulation: Closures allow for the creation of private variables, enabling data hiding and encapsulation. This is particularly useful in module patterns where certain data should not be expo...

Promise APIs + Interview Questions | all | allSettled | race | any

1. Introduction to Promises A Promise in JavaScript is an object representing the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises have three states: Pending: Initial state, neither fulfilled nor rejected. Fulfilled: The operation completed successfully. Rejected: The operation failed.  2. Promise APIs a. Promise.all() Purpose: Takes an iterable of promises and returns a single promise that resolves when all of the promises in the iterable have resolved, or rejects if any of the promises reject. Use Case: Useful when you want to wait for multiple asynchronous operations to complete successfully. Example: const p1 = Promise.resolve(1); const p2 = Promise.resolve(2); const p3 = Promise.resolve(3); Promise.all([p1, p2, p3])   .then(values => console.log(values)) // [1, 2, 3]   .catch(error => console.error(error)); Behavior: If all promises resolve, Promise.all resolves with an array of results. If any promise rejects,...

Callback Hell | Inversion of Control | Bad and Good Practices

 1. Introduction to Callbacks A callback is a function passed as an argument to another function and is executed after some operation is completed. Callbacks are commonly used in asynchronous operations like handling API requests, file I/O, or timers. Example: function fetchData(callback) {   setTimeout(() => {     const data = "Some data";     callback(data);   }, 1000); } fetchData((data) => {   console.log(data); // "Some data" }); 2. What is Callback Hell? Callback Hell (also known as the Pyramid of Doom) occurs when multiple nested callbacks are used to handle asynchronous operations, making the code difficult to read and maintain. Example of Callback Hell: fetchData((data1) => {   processData(data1, (data2) => {     saveData(data2, (data3) => {       displayData(data3, (data4) => {         console.log("Final result:", data4);       });     });  ...