đź“ť 30 Essential Javascript Interview Questions, with Detailed Answers From Easy to Hard

đź“ť 30 Essential Javascript Interview Questions, with Detailed Answers From Easy to Hard

·

24 min read

Prepare for your next job with these 30 essential JavaScript interview questions and answers, ranging from easy to hard. Perfect for mastering key concepts and skills.

This is a 5k+ words article. Checkout the original post to read with sticky Table of Contents for easier navigation or quick read

Hey there! I’m Syakir, a senior front-end engineer.

Back in the days when i was looking for job, i had plenty of interview experiences as Front-end / Javascript developer. I’ve been through the process many times and learned a lot along the way.

I know how tough technical interviews can be, especially when facing tricky JavaScript questions.

That’s why, in this article, I’ve put together this list of 30 essential javascript interview questions. I sort them from simple basics to more advanced topics you might encounter.

I’ve kept the answers comprehensive with code examples, but to the point, just like you’ll need them in an interview. By practicing these, you’ll feel more prepared and confident.

You might want to summarize key points in each question, so it will be easy to remember them.

This article is written with the help of AI. The question list are based on experiences and some references. The answers are fact-checked and the code examples are also manually tested.

Basic Javascript Interview Questions

1. What are the different data types in JavaScript?

In JavaScript, there are several data types:

Primitive types:

  • Number: Represents both integer and floating-point numbers

  • String: Represents textual data

  • Boolean: Represents true or false

  • Undefined: Represents a variable that has been declared but not assigned a value

  • Null: Represents a deliberate non-value

  • Symbol: Represents a unique identifier (introduced in ES6)

  • BigInt: Represents integers larger than 2^53 - 1 (introduced in ES11)

Object type:

  • Object: Represents a collection of key-value pairs

It's important to note that arrays and functions are also objects in JavaScript, but they have special behaviors and properties. Additionally, the typeof operator can be used to determine the type of a value, although it has some quirks (e.g., typeof null returns "object", which is a known legacy issue in the language).

Further reads:

2. What is hoisting in JavaScript?

Hoisting in JavaScript is a behavior where variable and function declarations are conceptually moved to the top of their containing scope during the compilation phase, before code execution.

Key points:

  1. Variable declarations using var are hoisted and initialized with undefined.

  2. Function declarations are fully hoisted, including their implementation.

  3. let and const declarations are hoisted but not initialized, creating a temporal dead zone.

  4. Only declarations are hoisted, not initializations or assignments.

  5. This behavior can lead to unexpected results, especially with var.

Here's a code example that demonstrates hoisting behavior in JavaScript:

console.log(x); // Outputs: undefined
console.log(y); // Throws ReferenceError
console.log(z); // Throws ReferenceError

console.log(foo()); // Outputs: "Hello from foo"
console.log(bar()); // Throws TypeError: bar is not a function

var x = 5;
let y = 10;
const z = 15;

function foo() {
    return 'Hello from foo';
}

var bar = function () {
    return 'Hello from bar';
};

console.log(x); // Outputs: 5
console.log(y); // Outputs: 10
console.log(z); // Outputs: 15
console.log(bar()); // Outputs: "Hello from bar"

Further read: Javascript Hoisting

3. What is the difference between var, let, and const?

var:

  1. Function-scoped or globally-scoped

  2. Hoisted and initialized with undefined

  3. Can be reassigned

  4. Can be declared without initialization

  5. Creates a property on the global object

let:

  1. Block-scoped

  2. Hoisted but not initialized (temporal dead zone)

  3. Can be reassigned

  4. Can be declared without initialization

  5. Does not create a property on the global object

const:

  1. Block-scoped

  2. Hoisted but not initialized (temporal dead zone)

  3. Cannot be reassigned after initialization

  4. Must be initialized upon declaration

  5. Does not create a property on the global object

A brief code example to illustrate these differences:

console.log(x); // Outputs: undefined (due to hoisting)
var x = 5;

console.log(y); // Throws ReferenceError (temporal dead zone)
let y = 10;

const z = 15;
z = 20; // Throws TypeError: Assignment to a constant variable

if (true) {
    var a = 1; // Function-scoped
    let b = 2; // Block-scoped
    const c = 3; // Block-scoped
}
console.log(a); // Outputs: 1
console.log(b); // Throws ReferenceError
console.log(c); // Throws ReferenceError

4. Explain how == and === differ in JavaScript.

In JavaScript, == and === are comparison operators, but they behave differently in terms of type comparison.

  • == (Loose Equality): It compares two values for equality after performing type coercion, which means it tries to convert the operands to the same type before comparing. This can lead to unexpected results when types differ.

      console.log(5 == '5'); // true, because '5' (string) is coerced to 5 (number)
      console.log(null == undefined); // true, because both are considered "empty" values
    
  • === (Strict Equality): It compares both the value and the type of the operands. No type coercion occurs, so the comparison is stricter.

      console.log(5 === '5'); // false, because number 5 is not the same type as string '5'
      console.log(null === undefined); // false, because they are of different types
    

Recommendation: Use === for comparisons to avoid unexpected type coercion and ensure accuracy.

5. What is a function in JavaScript?

A function in JavaScript is a block of reusable code designed to perform a specific task. It allows you to encapsulate a set of statements that can be executed whenever you call the function. Functions help organize code, promote reuse, and make programs more modular and maintainable.

Types of Functions in JavaScript:

  1. Function Declaration: A named function that can be called before its declaration due to hoisting.

     function add(a, b) {
         return a + b;
     }
    
  2. Function Expression: A function assigned to a variable. It’s not hoisted, so it can only be called after its definition.

     const multiply = function (a, b) {
         return a * b;
     };
    
  3. Arrow Function: A concise way to define functions, introduced in ES6. It also handles the this keyword differently.

     const subtract = (a, b) => a - b;
    
  4. Immediately Invoked Function Expression (IIFE): A function that runs immediately after it’s defined.

     (function () {
         console.log('IIFE executed!');
     })();
    

6. What is an arrow function, and how does it differ from a regular function?

An arrow function is a more concise syntax for writing functions in JavaScript, introduced in ES6. It offers several key differences from regular (traditional) functions, especially in terms of handling the this keyword and syntax.

An arrow function uses the => ("fat arrow") syntax.

const add = (a, b) => a + b;

console.log(add(2, 3)); // Output: 5

Key Differences Between Arrow Functions and Regular Functions:

  1. this Binding:

    • Arrow Functions: Do not have their own this. Instead, they inherit this from the surrounding (lexical) context in which they are defined.

    • Regular Functions: Have their own this, which can change based on how the function is called (e.g., method calls, event handlers).

    function Example() {
        this.value = 10;
        function regularFunction() {
            console.log(this.value); // undefined, because `this` refers to the global object (or undefined in strict mode)
        }

        const arrowFunction = () => {
            console.log(this.value); // 10, because `this` refers to the instance of Example
        };

        regularFunction();
        arrowFunction();
    }

    new Example();
  1. Constructors:

    • Arrow Functions: Cannot be used as constructors. If you try to use new with an arrow function, it will throw an error.

    • Regular Functions: Can be used as constructors with the new keyword to create object instances.

    const Person = (name) => {
        this.name = name;
    };
    // new Person('John'); // Error: Person is not a constructor

    function PersonRegular(name) {
        this.name = name;
    }
    const john = new PersonRegular('John');
    console.log(john.name); // John
  1. arguments Object:

    • Arrow Functions: Do not have their own arguments object. If you need access to the arguments of an arrow function, you have to use rest parameters (...args).

    • Regular Functions: Have access to the arguments object, which is an array-like object containing the function's parameters.

    const regularFunc = function () {
        console.log(arguments); // Logs the arguments passed
    };

    const arrowFunc = (...args) => {
        console.log(args); // Uses rest parameters to access arguments
    };

    regularFunc(1, 2, 3); // [1, 2, 3]
    arrowFunc(1, 2, 3); // [1, 2, 3]

Arrow functions are more concise and do not have their own this or arguments object.

Use arrow functions when you need a function to inherit this from the surrounding context, or when you need a shorter syntax. Use regular functions when you need dynamic this, constructors, or access to the arguments object.

7. What is null vs undefined in JavaScript?

In JavaScript, null and undefined both represent the absence of value but differ in usage:

  • undefined: Automatically assigned to uninitialized variables or missing function arguments.

      let a;
      console.log(a); // undefined
    
  • null: Explicitly set to indicate "no value."

      let b = null;
      console.log(b); // null
    

Key Differences:

  • null == undefined is true, but null === undefined is false.

  • undefined is a type, while null is an object.

Use null to explicitly clear values; let undefined be the default for uninitialized ones.

8. What are template literals?

Template literals in JavaScript use backticks (` ) for flexible string creation. Key features:

  1. String Interpolation: Embed variables and expressions with ${}.

     const name = 'Syakir';
     console.log(`Hello, ${name}!`); // Hello, Syakir!
    
  2. Multiline Strings: Easily create strings over multiple lines.

     const msg = `This is
     a multiline string.`;
    
  3. Expression Evaluation: Directly evaluate expressions inside strings.

     const sum = `The sum is ${5 + 10}`; // The sum is 15
    

Summary:

  • Backticks and ${} allow dynamic and multiline strings efficiently.

9. What is a closure in JavaScript?

A closure in JavaScript is a feature where a function retains access to its lexical scope, even after the function has finished executing. This allows the inner function to access variables from its outer (enclosing) function.

Key Points:

  1. Lexical Scope: Closures capture variables from their creation context.

  2. Encapsulation: They enable data encapsulation and can be used to create private variables.

function createCounter() {
    let count = 0; // `count` is in the lexical scope of `increment`

    return function increment() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2

In this example:

  • increment retains access to count, even after createCounter has finished executing.

  • count is not accessible directly from outside createCounter, demonstrating encapsulation.

Closures allow functions to access variables from their outer scope, enabling powerful patterns like data hiding and stateful functions.

10. What is the difference between function declarations and function expressions?

Function declarations and function expressions are two ways to define functions in JavaScript. Declarations use function keyword, while expressions assign a function to a variable.

  • Function Declarations:

      console.log(greet('Alice')); // Output: Hello, Alice!
    
      function greet(name) {
          return `Hello, ${name}!`;
      }
    
    • Hoisted; callable before defined.

    • Available throughout the entire scope.

  • Function Expressions:

      console.log(greet('Alice')); // Error: greet is not defined
    
      const greet = function (name) {
          return `Hello, ${name}!`;
      };
    
      console.log(greet('Alice')); // Output: Hello, Alice!
    
    • Not hoisted; callable only after defined.

    • Scoped to the variable holding the function.

Moderate Javascript Interview Questions

11. What is the this keyword in JavaScript, and how does it behave in different contexts?

The this keyword in JavaScript refers to the context in which a function is executed. Its value depends on how the function is called:

  1. In Global Context,this refers to the global object (window in browsers).
console.log(this); // window (in browsers)
  1. In Object Methods, this refers to the object the method is called on.
const obj = {
    value: 42,
    showValue() {
        console.log(this.value); // 42
    }
};
obj.showValue();
  1. In a Constructor Function, this refers to the newly created instance.
function Person(name) {
    this.name = name;
}
const person = new Person('Alice');
console.log(person.name); // Alice
  1. In Arrow Functions, this refers to their enclosing context. Arrow functions do not have their own this.
function outer() {
    this.value = 10;
    const inner = () => console.log(this.value); // 10
    inner();
}
outer();
  1. In Event Handlers, this refers to the element that triggered the event.
document.getElementById('myBtn').addEventListener('click', function () {
    console.log(this); // <button id="myBtn">
});

12. What is event delegation?

Event delegation is a technique where you attach a single event listener to a parent element to handle events for its child elements, instead of attaching listeners to each child. It leverages event bubbling and can improve performance and memory usage, especially for dynamically created elements.

document.getElementById('parent-list').addEventListener('click', function (e) {
    if (e.target && e.target.nodeName == 'LI') {
        console.log('List item ', e.target.id, ' was clicked');
    }
});

This code adds one listener to the parent <ul>, handling clicks on all child <li> elements, even those added dynamically after the initial page load.

13. What are promises, and how do they differ from callbacks?

Promises are objects representing the eventual completion or failure of an asynchronous operation. They provide a cleaner, more manageable way to handle asynchronous code compared to callbacks.

Callbacks are functions passed as arguments to be executed after a task completes, but they can lead to "callback hell" when nested.

// Callback
function fetchData(callback) {
    setTimeout(() => {
        callback('Data loaded');
    }, 1000);
}

fetchData((result) => {
    console.log(result); // Data loaded
});

// Promis
function fetchData() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('Data loaded');
        }, 1000);
    });
}

fetchData().then((result) => {
    console.log(result); // Data loaded
});

Key Differences

  • Chaining: Promises allow easy chaining of asynchronous operations.

  • Error handling: Promises use .catch() for centralized error handling.

  • State: Promises have clear states (pending, fulfilled, rejected).

  • Readability: Promises often lead to more readable code, avoiding "callback hell".

14. What is destructuring in JavaScript?

Destructuring in JavaScript is a syntax that allows you to extract values from arrays or properties from objects and assign them to variables in a more concise way. It provides a convenient method to unpack values from data structures into distinct variables.

Key points:

  1. Works with both arrays and objects

  2. Allows default values

  3. Can be used in function parameters

  4. Supports nested destructuring

  5. Enables easy variable swapping

// Object destructuring
const person = { name: 'John', age: 30, city: 'New York' };
const { name, age } = person;
console.log(name, age); // John 30

// Array destructuring
const colors = ['red', 'green', 'blue'];
const [firstColor, secondColor] = colors;
console.log(firstColor, secondColor); // red green

// Function parameter destructuring
function printCoordinates({ x = 0, y = 0 }) {
    console.log(`X: ${x}, Y: ${y}`);
}
printCoordinates({ x: 10, y: 20 }); // X: 10, Y: 20

This syntax enhances code readability and reduces the amount of code needed to access data from complex structures.

Further reading:

15. What is the spread operator (...), and how is it used?

The spread operator (...) in JavaScript is used to expand iterable objects into individual elements. It has multiple use cases:

  1. Array manipulation: Copying, concatenating, or inserting elements.

  2. Function arguments: Passing array elements as separate arguments.

  3. Object literals: Copying properties from one object to another.

  4. Rest parameters: Collecting multiple arguments into an array.

Example demonstrating these uses:

// Array manipulation
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

// Function arguments
const nums = [1, 2, 3];
console.log(Math.max(...nums)); // 3

// Object literals
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }

// Rest parameters
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6

The spread operator enhances code readability and provides a concise way to work with arrays and objects.

16. What are higher-order functions?

Higher-order functions are functions that can take other functions as arguments or return functions as their results. They enable powerful abstractions and are a key concept in functional programming.

Key points:

  1. Can accept functions as arguments

  2. Can return functions

  3. Enable function composition and abstraction

  4. Common in array methods like map, filter, and reduce

  5. Facilitate code reuse and modularity

Example:

// Higher-order function that takes a function as an argument
function applyOperation(x, y, operation) {
    return operation(x, y);
}

// Functions to be passed as arguments
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

console.log(applyOperation(5, 3, add)); // Outputs: 8
console.log(applyOperation(5, 3, multiply)); // Outputs: 15

// Higher-order function that returns a function
function createMultiplier(factor) {
    return (number) => number * factor;
}

const double = createMultiplier(2);
console.log(double(5)); // Outputs: 10

This example demonstrates both accepting functions as arguments and returning functions, showcasing the versatility of higher-order functions.

17. What is prototypal inheritance in JavaScript?

Prototypal inheritance in JavaScript is a mechanism where objects can inherit properties and methods from other objects. Each object has an internal link to another object called its prototype. When a property is accessed on an object and not found, JavaScript looks for it in the prototype chain.

Key points:

  1. Objects inherit from objects.

  2. There's a prototype chain.

  3. It's dynamic - changes to the prototype affect all inheriting objects.

  4. It's the basis for JavaScript's object-oriented programming model.

// Constructor function
function Animal(name) {
    this.name = name;
}

// Method on the prototype
Animal.prototype.sayHello = function () {
    return `Hello, I'm ${this.name}`;
};

// Creating an instance
const cat = new Animal('Whiskers');

console.log(cat.sayHello()); // Outputs: "Hello, I'm Whiskers"
console.log(cat.__proto__ === Animal.prototype); // true

In this example, cat inherits the sayHello method from Animal.prototype.

18. What is the difference between map(), forEach(), and filter()?

map(), forEach(), and filter() are array methods in JavaScript with distinct purposes:

  1. map(): Transforms each element of an array, returning a new array of the same length.

  2. forEach(): Executes a function on each array element, but doesn't return a new array.

  3. filter(): Creates a new array with elements that pass a test function.

const numbers = [1, 2, 3, 4, 5];

// map()
const doubled = numbers.map((num) => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// forEach()
numbers.forEach((num) => console.log(num)); // Logs: 1, 2, 3, 4, 5

// filter()
const evens = numbers.filter((num) => num % 2 === 0);
console.log(evens); // [2, 4]

Key differences:

  • map() and filter() return new arrays; forEach() doesn't.

  • map() transforms elements; filter() selects elements; forEach() just iterates.

  • Use map() for transformation, filter() for selection, and forEach() for side effects.

19. What are call(), apply(), and bind() methods?

call(), apply(), and bind() are methods used to manipulate the this context in JavaScript functions:

  1. call(): Invokes a function with a specified this value and arguments provided individually.

  2. apply(): Similar to call(), but accepts arguments as an array.

  3. bind(): Returns a new function with a fixed this value, without executing it immediately.

Example:

const person = { name: 'John' };

function greet(message) {
    console.log(`${message}, ${this.name}!`);
}

// call
greet.call(person, 'Hello'); // Output: Hello, John!

// apply
greet.apply(person, ['Hi']); // Output: Hi, John!

// bind
const boundGreet = greet.bind(person);
boundGreet('Hey'); // Output: Hey, John!

These methods are crucial for controlling function context, especially in scenarios involving callbacks, event handlers, or when borrowing methods from other objects.

20. What is the event loop in JavaScript?

The event loop in JavaScript is a mechanism that handles asynchronous operations. It's a single-threaded process that manages code execution, events, and asynchronous tasks. It continuously:

  1. Checks the call stack: If empty, it moves on.

  2. Processes tasks from the task queue: These are callbacks from asynchronous operations (e.g., network requests, timers).

  3. Waits for new events: Like user interactions or browser messages.

This non-blocking approach allows JavaScript to feel responsive even while handling long-running tasks.

console.log('Start script'); // Main thread

setTimeout(() => {
    console.log('This is async process');
}, 0);

console.log('End'); // Runs before timeout

// Event loop will add the timeout callback to the task queue to be executed later.

In this example, setTimeout is placed in the task queue and only executed after the synchronous code (like console.log('End')) finishes. The event loop handles moving the callback from the queue to the call stack when ready.

21. What is the difference between synchronous and asynchronous code in JavaScript?

The difference between synchronous and asynchronous code in JavaScript lies in how tasks are executed:

  • Synchronous code is executed line by line, blocking further execution until the current task is completed. It waits for each operation to finish before moving on to the next one.

  • Asynchronous code allows the program to continue executing without waiting for an operation (like fetching data or reading files) to complete. The result of the async operation is handled later, often with callbacks, promises, or async/await.

// Synchronous
console.log('Start');
synchronousFunction(); // This blocks further execution until finished
console.log('End');

// Asyncronous
console.log('Start');
fetchData().then((data) => {
    console.log('Data received');
});
console.log('End');
// Output will be:
// Start
// End
// Data received

In the async example, fetchData runs in the background, allowing console.log('End') to execute without waiting for it.

22. What are async and await in JavaScript?

async and await are modern JavaScript features for handling asynchronous operations in a cleaner, more readable way compared to promises.

Key points:

  1. async functions return a promise, making them inherently asynchronous.

  2. await pauses the execution inside async functions, waiting for a promise to resolve or reject.

  3. They eliminate the need for .then() chaining, improving code readability.

  4. Error handling can be done using try...catch within async functions.

// Example using async/await
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error fetching data:', error);
    }
}

fetchData();

In this example, await pauses the function until the fetch resolves, making the code easier to follow than using .then() chains.

23. What is the difference between shallow copy and deep copy in JavaScript?

In JavaScript, shallow copy and deep copy refer to how objects and arrays are duplicated.

  • Shallow Copy: Copies the object’s top level. Nested objects or arrays are still referenced, not cloned. Changes to nested structures affect the original object.

  • Deep Copy: Recursively copies all levels of the object, creating entirely new instances of nested objects and arrays. Changes do not affect the original.

// Shallow copy
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };

shallowCopy.b.c = 3;
console.log(original.b.c); // 3 (original is affected)

// Deep copy
const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.b.c = 3;
console.log(original.b.c); // 2 (original is not affected)

Further reading:

24. What is a module in JavaScript, and how are they implemented?

Modules in JavaScript are a way to encapsulate and organize code into separate files. They help manage dependencies and improve code maintainability by allowing functions, objects, or values to be imported and exported between files.

Key points:

  1. Encapsulation: Modules keep code isolated in separate files.

  2. Imports and Exports: Use import and export to share code between files.

  3. ES6 Standard: Modern JavaScript uses ES6 module syntax.

module.js

// Exporting a function
export function greet(name) {
    return `Hello, ${name}`;
}

main.js

// Importing the function
import { greet } from './module.js';

console.log(greet('World')); // Outputs: "Hello, World"

In this example, greet is exported from module.js and imported into main.js, demonstrating how modules allow code sharing and organization.

Advanced Javascript Interview Questions

25. How does garbage collection work in JavaScript?

Garbage collection in JavaScript is the process of automatically managing memory by cleaning up unused objects and freeing their memory space. JavaScript uses a form of garbage collection called mark-and-sweep.

Key points:

  1. Marking: The garbage collector identifies which objects are still in use by marking them, starting from global objects and roots (like the call stack).

  2. Sweeping: It then identifies objects that are no longer reachable (not marked) and reclaims their memory.

  3. Automatic: Garbage collection is automatic and managed by the JavaScript engine, so developers don't need to manually free memory.

function createObject() {
    let largeArray = new Array(1000000).fill('data'); // Large object
    return function () {
        console.log('Still here');
    };
}

let keepObject = createObject(); // `largeArray` is reachable

// After `keepObject` is no longer referenced:
keepObject = null; // `largeArray` is eligible for garbage collection

// The garbage collector will clean up `largeArray` eventually

In this example, after keepObject is set to null, largeArray becomes eligible for garbage collection, though the exact timing of cleanup is managed by the JavaScript engine.

26. What is the new keyword, and how does it work in JavaScript?

The new keyword in JavaScript is used to create instances of user-defined objects or built-in objects. It sets up a new object, binds this to that object, and initializes the object with properties and methods defined in a constructor function.

Key points:

  1. Creates a new object: A new, empty object is created.

  2. Sets the prototype: The prototype of the new object is set to the prototype of the constructor function.

  3. Binds this: Inside the constructor, this refers to the new object.

  4. Returns the object: The newly created object is returned (unless the constructor explicitly returns a different object).

function Person(name) {
    this.name = name;
    this.sayHello = function () {
        return `Hello, I'm ${this.name}`;
    };
}

// Creating an instance
const john = new Person('John');

console.log(john.sayHello()); // Outputs: "Hello, I'm John"
console.log(john instanceof Person); // true

In this example, new Person('John') creates a new object with name set to 'John' and sayHello as a method, demonstrating how the new keyword sets up and initializes the object.

27. What is Object.create(), and how does it differ from using a constructor function?

Object.create() is a method that creates a new object with the specified prototype object and properties. It allows for setting up inheritance without needing a constructor function.

Key points:

  1. Directly sets prototype: Object.create() sets the prototype of the new object directly, providing more control over inheritance.

  2. No need for a constructor: Unlike constructor functions, it does not involve the new keyword or this binding.

  3. Flexible prototype setup: You can define the prototype and additional properties in one step.

// Object.create example
// Define a prototype object
const animal = {
    speak() {
        return `I am a ${this.type}`;
    }
};

// Create a new object with `animal` as its prototype
const dog = Object.create(animal);
dog.type = 'dog';

console.log(dog.speak()); // Outputs: "I am a dog"
console.log(Object.getPrototypeOf(dog) === animal); // true

// Constructor example
// Constructor function
function Animal(type) {
    this.type = type;
}

Animal.prototype.speak = function () {
    return `I am a ${this.type}`;
};

// Create a new instance
const dog2 = new Animal('dog');

console.log(dog2.speak()); // Outputs: "I am a dog"
console.log(dog2 instanceof Animal); // true

Difference:

  • Object.create() directly sets the prototype without a constructor, offering a more flexible way to create objects with a specific prototype.

  • Constructor functions create instances that inherit from the prototype chain set up via prototype.

28. What is memoization in JavaScript, and how can it improve performance?

Memoization in JavaScript is an optimization technique that caches the results of expensive function calls and returns the cached result when the same inputs occur again. This reduces the need for repeated computations, improving performance for functions with overlapping inputs.

Key points:

  1. Caches Results: Stores function results based on input arguments.

  2. Reduces Computation: Avoids redundant calculations.

  3. Improves Performance: Especially useful for functions that are called frequently with the same arguments.

function memoize(fn) {
    const cache = new Map();
    return function (...args) {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = fn(...args);
        cache.set(key, result);
        return result;
    };
}

// Expensive function
function factorial(n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

const memoizedFactorial = memoize(factorial);

console.log(memoizedFactorial(5)); // 120
console.log(memoizedFactorial(5)); // 120 (cached result)

In this example, memoize caches the results of the factorial function. Subsequent calls with the same argument return the cached result instead of recomputing it.

29. What is the difference between classical inheritance and prototypal inheritance?

Classical Inheritance vs. Prototypal Inheritance

Classical Inheritance is based on the concept of classes and objects. Classes are blueprints for creating objects, and inheritance is achieved by creating subclasses that extend a base class. This is common in object-oriented programming languages like Java or C++.

Prototypal Inheritance in JavaScript allows objects to directly inherit from other objects. Instead of creating subclasses, objects can be created based on existing objects, and their prototype can be modified to include inherited properties and methods.

Key points:

  1. Classical Inheritance uses classes and constructors, with a clear hierarchy of base and derived classes.

  2. Prototypal Inheritance uses prototypes, where objects inherit directly from other objects.

  3. Classical Inheritance is often more rigid and requires defining classes upfront.

  4. Prototypal Inheritance is more flexible and dynamic, allowing for more direct object-to-object inheritance.

// Classical Inheritance in JavaScript (using ES6 classes)
class Animal {
    constructor(name) {
        this.name = name;
    }
    speak() {
        return `I'm ${this.name}`;
    }
}

class Dog extends Animal {
    bark() {
        return 'Woof!';
    }
}

const dog = new Dog('Rex');
console.log(dog.speak()); // Outputs: "I'm Rex"
console.log(dog.bark()); // Outputs: "Woof!"

// Prototypal Inheritance
const animal = {
    speak() {
        return `I'm ${this.name}`;
    }
};

const dog2 = Object.create(animal);
dog2.name = 'Rex';
dog2.bark = function () {
    return 'Woof!';
};

console.log(dog2.speak()); // Outputs: "I'm Rex"
console.log(dog2.bark()); // Outputs: "Woof!"

In the classical example, Dog inherits from Animal using class-based inheritance. In the prototypal example, dog2 inherits from animal directly using the prototype chain.

30. What are generator functions, and how are they used?

Generator functions are special functions in JavaScript that can pause their execution and resume later. They are defined using the function* syntax and use the yield keyword to produce a series of values.

Key points:

  1. Defined with function*: Creates a generator function.

  2. Uses yield: Pauses execution and returns a value.

  3. Returns an iterator: Allows iterating over values produced by yield.

  4. Resumable: Can resume from where it was paused using .next().

function* numberGenerator() {
    let num = 0;
    while (true) {
        yield num++;
    }
}

const generator = numberGenerator();

console.log(generator.next()); // { value: 0, done: false }
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: 1  false }

In this example, numberGenerator is a generator function that yields an infinite sequence of numbers. The next() method is used to resume the generator and get the next value.

Conclusion

You’ve now covered 30 essential JavaScript interview questions. From basics to advanced concepts, these questions should give you a solid foundation for your next interview. Practicing these will help you explain your thought process clearly and confidently.

If you found this helpful, feel free to share it with others or drop a comment below with your thoughts or questions. I’d love to hear from you.

Good luck with your interviews!