js
Common

1. What is the difference between let and var?

Featurevarlet
AvailabilityAvailable from the beginning of JavaScriptIntroduced in ES6
ScopeFunction scopeBlock scope
HoistingHoisted and initialized with undefinedHoisted but not initialized (Temporal Dead Zone)
RedeclarationCan be redeclared in the same scopeCannot be redeclared in the same scope
Global object propertyBecomes a property of the global object (window)Does not become a property of the global object

Example:

function userDetails(username) {
  if (username) {
    console.log(salary); // undefined (hoisted)
    console.log(age); // ReferenceError: Cannot access 'age' before initialization
    let age = 30;
    var salary = 10000;
  }
  console.log(salary); // 10000 (function scope)
  console.log(age); // ReferenceError: age is not defined (block scope)
}
userDetails("John");

2. What is Hoisting?

Hoisting is JavaScript's behavior where variable and function declarations are moved to the top of their containing scope during the compilation phase, before the code is executed.

console.log(myVar); // undefined
var myVar = 10;
console.log(myVar); // 10
 
// Function declaration (fully hoisted)
myFun(); // "Hi"
function myFun() {
    console.log('Hi');
}
 
// Function expression (not hoisted)
mySum(); // ReferenceError: mySum is not defined
const mySum = function(a, b) {
  return a + b;
};

Key Points:

  • Function declarations: Fully hoisted, can be called before definition
  • Function expressions: Not hoisted
  • var variables: Hoisted but initialized with undefined
  • let/const variables: Hoisted but not initialized (Temporal Dead Zone)
  • Class declarations: Not hoisted

3. What is the difference between Reference types and Primitive types?

Primitive Types

Store the actual value directly. They are immutable.

let num = 5;          // Number
let str = 'Hi';       // String
let bool = true;      // Boolean
let und = undefined;  // Undefined (variable declared but not assigned)
let nul = null;       // Null (intentional absence of value)
let sym = Symbol('id'); // Symbol (unique identifier)
let big = 1234567890123456789012345678901234567890n; // BigInt

Reference Types

Store a reference (memory address) to the value. They are mutable.

let arr = [1, 2, 3];                    // Array
let obj = {name: "alice", age: 22};     // Object
let func = function() {                 // Function
  console.log('hi');
};
let date = new Date();                  // Date

Key Differences:

Primitive TypesReference Types
Stored in Stack memoryStored in Heap memory
Copy creates a new valueCopy creates a reference to the same value
Compared by valueCompared by reference
ImmutableMutable

4. Difference between arrow functions and regular functions

Regular Functions

function regularFunc() {
  console.log(this); // Depends on how called
}
 
const obj = {
  method: function() {
    console.log(this); // Refers to obj
  }
};

Characteristics:

  • Have their own this context
  • Can be used as constructors with new
  • Have arguments object
  • Have prototype property
  • Can use yield in generator functions

Arrow Functions

const arrowFunc = () => {
  console.log(this); // Inherits from parent scope
};
 
const obj = {
  method: () => {
    console.log(this); // Refers to parent scope, not obj
  }
};

Characteristics:

  • Inherit this from surrounding lexical scope
  • Cannot be used as constructors (no new keyword)
  • No arguments object (use rest parameters instead)
  • No prototype property
  • Shorter syntax
  • Implicit return when using concise body

5. How does the this keyword work in different contexts?

The this keyword refers to different objects depending on how and where it's used.

// 1. Global context
console.log(this); // In browser: Window object
                  // In Node.js: Global object
 
// 2. Function context (non-strict mode)
function showThis() {
    console.log(this); // Window object (in browser)
}
 
// 3. Function context (strict mode)
function showThisStrict() {
    'use strict';
    console.log(this); // undefined
}
 
// 4. Object method
const obj = {
    name: "John",
    method: function() {
        console.log(this.name); // "John"
    }
};
 
// 5. Constructor function
function Person(name) {
    this.name = name;
}
const person = new Person("Alice");
console.log(person.name); // "Alice"
 
// 6. Event handlers
button.addEventListener('click', function() {
    console.log(this); // The button element
});
 
// 7. Arrow function (lexical this)
const arrowObj = {
    name: "Bob",
    method: () => {
        console.log(this.name); // undefined (inherits from outer scope)
    }
};

Rules for this binding:

  1. Default binding: Global object (or undefined in strict mode)
  2. Implicit binding: Object to the left of the dot
  3. Explicit binding: Using call(), apply(), or bind()
  4. new binding: The newly created object
  5. Arrow functions: Lexical this from enclosing scope

6. Difference between Synchronous and Asynchronous JavaScript

Synchronous JavaScript

  • Code executes sequentially, line by line
  • Each operation must complete before the next one starts
  • Blocking operations can freeze the UI
console.log("First");  // Executes first
console.log("Second"); // Waits for first to complete
console.log("Third");  // Waits for second to complete
// Output: First, Second, Third

Asynchronous JavaScript

  • Code can continue executing while waiting for operations to complete
  • Non-blocking operations allow the UI to remain responsive
  • Uses callbacks, promises, or async/await
console.log("First"); // Executes immediately
 
setTimeout(() => {
    console.log("Second"); // Executes after 1 second
}, 1000);
 
console.log("Third"); // Executes immediately (doesn't wait for timeout)
// Output: First, Third, Second

JavaScript's Event Loop

JavaScript is single-threaded but handles asynchronicity through:

  1. Call Stack: Where synchronous code executes
  2. Web APIs: Browser-provided APIs for async operations
  3. Callback Queue: Holds callbacks waiting to be executed
  4. Event Loop: Checks if call stack is empty, then pushes callbacks from queue

Common Async Patterns

// 1. Callbacks (older style)
function fetchData(callback) {
    setTimeout(() => {
        callback("Data received");
    }, 1000);
}
 
// 2. Promises (ES6)
function fetchDataPromise() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data received");
        }, 1000);
    });
}
 
// 3. Async/Await (ES2017)
async function getData() {
    const data = await fetchDataPromise();
    console.log(data);
}

Key Concepts:

  • Callbacks: Functions passed as arguments to be executed later
  • Promises: Objects representing eventual completion/failure of async operations
  • Async/Await: Syntactic sugar for promises, makes async code look synchronous
  • Microtasks vs Macrotasks: Promise callbacks (microtasks) have higher priority than setTimeout (macrotasks)

Summary

JavaScript's core concepts like variable scoping, hoisting, type systems, function behavior, this context, and asynchronicity are fundamental to writing effective code and are frequently tested in interviews. Understanding these concepts helps you write more predictable, efficient, and maintainable JavaScript applications.