DEV Community

Cover image for Must-know Basic JavaScript Interview Questions for Freshers
GreatFrontEnd Team
GreatFrontEnd Team

Posted on

Must-know Basic JavaScript Interview Questions for Freshers

Breaking into the web development world as a junior developer can feel like an uphill climb, especially when you're gearing up for technical interviews. JavaScript, being the cornerstone of web development, often takes center stage in these evaluations. To make your prep smoother and give you a confidence boost, we’ve curated a list of 50 essential JavaScript interview questions and answers that frequently come up. Let’s help you shine in your next interview!

💡 Getting ready for JavaScript coding interviews? Join over 500,000 front-end engineers on GreatFrontEnd—your go-to platform for mastering front-end interviews! 🚀

Prepare with materials designed by ex-FAANG interviewers for a truly elite experience. Enjoy an in-browser coding workspace, along with official solutions and tests for every question! 🖥️✨

📌 Dive in now:

Ace your interviews with confidence! 💡

1. What is "Hoisting" in JavaScript?

In JavaScript, "hoisting" refers to the process where variable and function declarations are moved to the top of their scope during the compile phase. While declarations are hoisted, initializations are not. This behavior can cause unexpected results if not well understood.

Example with var:

console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1
Enter fullscreen mode Exit fullscreen mode

What happens internally:

var foo;
console.log(foo); // undefined
foo = 1;
console.log(foo); // 1
Enter fullscreen mode Exit fullscreen mode

Behavior with let, const, and class:

Variables declared with let, const, and class are hoisted but remain in a "temporal dead zone" until they are initialized. Accessing them before declaration results in a ReferenceError.

console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 'local';

console.log(z); // ReferenceError: Cannot access 'z' before initialization
const z = 'local';

console.log(Foo); // ReferenceError: Cannot access 'Foo' before initialization
class Foo {
  constructor() {}
}
Enter fullscreen mode Exit fullscreen mode

Function Expressions:

For function expressions, the variable declaration is hoisted, but the function definition is not.

console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function

var bar = function () {
  console.log('BARRRR');
};
Enter fullscreen mode Exit fullscreen mode

Function Declarations:

Both the declaration and definition are hoisted, making the function available before its definition.

console.log(foo); // [Function: foo]
foo(); // 'FOOOOO'

function foo() {
  console.log('FOOOOO');
}
Enter fullscreen mode Exit fullscreen mode

Import Statements:

Imports are hoisted across the entire module. However, side effects occur before any other code in the module.

foo.doSomething(); // Works normally.
import foo from './modules/foo';
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding hoisting is crucial because it highlights how JavaScript handles variable and function declarations under the hood. Many interview scenarios focus on edge cases like temporal dead zones or the difference between var and let/const. Being able to explain and demonstrate hoisting reflects strong foundational knowledge.

Bonus Insights

  • Avoid var to reduce confusion around hoisting; prefer let and const.
  • Always declare variables at the beginning of their scope to improve code clarity and predictability.
  • Be cautious with function expressions, as they may behave differently from function declarations due to partial hoisting.

Discover more about "hoisting" and master JavaScript fundamentals

2. What's the Difference Between let, var, and const in JavaScript?

JavaScript provides three ways to declare variables: let, var, and const. Each has unique characteristics that determine how they behave in different scenarios, such as scope, initialization, redeclaration, and reassignment.

Scope

  • var: Function-scoped or globally scoped.
  • let and const: Block-scoped, meaning they are only accessible within the nearest set of curly braces.

Example:

function foo() {
  var bar = 1;
  let baz = 2;
  const qux = 3;

  console.log(bar); // 1
  console.log(baz); // 2
  console.log(qux); // 3
}

console.log(bar); // ReferenceError
console.log(baz); // ReferenceError
console.log(qux); // ReferenceError
Enter fullscreen mode Exit fullscreen mode
if (true) {
  var bar = 1;
  let baz = 2;
  const qux = 3;
}

console.log(bar); // 1
console.log(baz); // ReferenceError
console.log(qux); // ReferenceError
Enter fullscreen mode Exit fullscreen mode

Initialization

  • var and let: Can be declared without an initial value.
  • const: Must be initialized during declaration.

Example:

var foo; // Ok
let bar; // Ok
const baz; // SyntaxError
Enter fullscreen mode Exit fullscreen mode

Redeclaration

  • var: Allows redeclaration within the same scope.
  • let and const: Do not allow redeclaration within the same scope.

Example:

var foo = 1;
var foo = 2; // Ok

let baz = 3;
let baz = 4; // SyntaxError
Enter fullscreen mode Exit fullscreen mode

Reassignment

  • var and let: Permit reassignment.
  • const: Does not permit reassignment.

Example:

var foo = 1;
foo = 2; // Ok

let bar = 3;
bar = 4; // Ok

const baz = 5;
baz = 6; // TypeError
Enter fullscreen mode Exit fullscreen mode

Access Before Declaration

  • var: Hoisted and initialized to undefined, making it accessible before its declaration.
  • let and const: Hoisted but uninitialized, causing a ReferenceError if accessed before declaration.

Example:

console.log(foo); // undefined
var foo = 'foo';

console.log(baz); // ReferenceError
let baz = 'baz';

console.log(bar); // ReferenceError
const bar = 'bar';
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding the differences between let, var, and const is essential because it demonstrates your knowledge of variable behavior in various contexts. Interviewers often use these concepts to evaluate your understanding of scoping, hoisting, and immutability, which are foundational for writing clean and efficient JavaScript code.

Bonus Insights

  • Always prefer const for values that don’t change to communicate immutability.
  • Use let for variables that will be reassigned.
  • Avoid var as it may introduce bugs due to its hoisting and global scope behavior.
  • Remember that while const prevents reassignment, objects and arrays declared with const can still have their contents modified.

Deep dive into the differences between let, var, and const on GreatFrontEnd

3. How Does == Differ from === in JavaScript?

In JavaScript, == and === are used to compare values, but they work in distinct ways. Understanding their differences is crucial to avoiding unexpected bugs.

Equality Operator (==)

The == operator compares two values after performing type coercion, meaning it converts them to a common type before comparison. While this can be convenient, it often leads to unexpected results.

Examples:

42 == '42'; // true
0 == false; // true
null == undefined; // true
[] == false; // true
'' == false; // true
Enter fullscreen mode Exit fullscreen mode

Strict Equality Operator (===)

The === operator, also known as the strict equality operator, does not perform type coercion. Both the value and type must be identical for the comparison to return true.

Examples:

42 === '42'; // false
0 === false; // false
null === undefined; // false
[] === false; // false
'' === false; // false
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Use === in most cases to avoid issues with type coercion. This ensures your comparisons are both value- and type-accurate.

  • Use == only when comparing against null or undefined for convenience in cases where type conversion is acceptable:

    var a = null;
    console.log(a == null); // true
    console.log(a == undefined); // true
    

Why It Matters in Interviews

Knowing when and why to use == versus === demonstrates your understanding of JavaScript's type system. This question often tests whether candidates grasp subtle but critical language details that can lead to bugs if misunderstood.

Bonus Insights

  • Type coercion in == can sometimes be helpful but often introduces hidden complexities. Use it sparingly and with caution.
  • Always default to === unless you have a specific reason to rely on type conversion.

Learn more about == and === in JavaScript on GreatFrontEnd

4. How Does the Event Loop Work in JavaScript?

The event loop is at the heart of JavaScript's ability to handle asynchronous operations in a single-threaded environment, enabling non-blocking code execution. Understanding this concept is key to writing efficient and responsive applications.

Key Components of the Event Loop

1. Call Stack:

  • Manages function execution in Last In, First Out (LIFO) order.
  • Functions are pushed onto the stack when called and popped off when they complete.

2. Web APIs/Node.js APIs:

  • Handle asynchronous tasks such as setTimeout, HTTP requests, or DOM events in the background.

3. Task Queue (Macrotask Queue):

  • Stores tasks like setTimeout, setInterval, or UI event callbacks that are ready to execute.

4. Microtask Queue:

  • Contains high-priority tasks such as Promise callbacks and MutationObserver tasks.
  • Always processed before macrotasks.

How the Event Loop Operates

  1. Synchronous Code Execution:

    • Starts with synchronous tasks being placed on and executed from the call stack.
  2. Asynchronous Tasks:

    • Offloaded to Web APIs/Node.js APIs to execute in the background.
  3. Task Completion:

    • When asynchronous tasks complete, they are queued in the macrotask or microtask queue.
  4. Event Loop Monitoring:

    • Processes tasks from the call stack first.
    • When the stack is empty:
      • Executes all tasks in the microtask queue.
      • Processes one macrotask and re-checks the microtask queue.
    • This cycle continues indefinitely.

Example

console.log('Start');

setTimeout(() => {
  console.log('Timeout 1');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise 1');
});

setTimeout(() => {
  console.log('Timeout 2');
}, 0);

console.log('End');
Enter fullscreen mode Exit fullscreen mode

Console Output:

Start
End
Promise 1
Timeout 1
Timeout 2
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. Start and End are logged first since they are synchronous.
  2. The promise resolves, placing Promise 1 in the microtask queue, which is processed before macrotasks.
  3. Timeout 1 and Timeout 2 are executed later as macrotasks.

Why It Matters in Interviews

Understanding the event loop showcases your ability to handle asynchronous behavior effectively, a critical skill for frontend developers. Questions about the event loop test your grasp of JavaScript's concurrency model and its impact on user experience.

Bonus Insights

  • Use microtasks (e.g., Promise) for tasks that need to execute quickly after the current operation.
  • Recognize when tasks belong to the macrotask queue to avoid unexpected delays.
  • Understanding the event loop allows you to write non-blocking, high-performance applications.

Learn more about the JavaScript event loop on GreatFrontEnd

5. What is Event Delegation in JavaScript?

Event delegation is a powerful technique that allows you to manage events efficiently on multiple child elements by attaching a single event listener to a parent element. It’s particularly useful for handling dynamic or large sets of elements like lists or grids.

How Event Delegation Works

  1. Attach Listener to Parent:

    • Instead of adding separate listeners to each child, attach one listener to a common ancestor.
  2. Event Bubbling:

    • Events triggered on child elements bubble up to the parent, enabling centralized handling.
  3. Identify the Target:

    • Use event.target to determine which specific child element triggered the event.
  4. Perform Desired Action:

    • Execute your logic based on the event target.

Why Use Event Delegation?

  • Improved Performance: Reduces the number of event listeners, saving memory and improving efficiency.
  • Handles Dynamic Elements: Automatically supports child elements added or removed after the event listener is attached.

Example: Click Handling on List Items

// HTML:
// <ul id="item-list">
//   <li>Item 1</li>
//   <li>Item 2</li>
//   <li>Item 3</li>
// </ul>

const itemList = document.getElementById('item-list');

itemList.addEventListener('click', (event) => {
  if (event.target.tagName === 'LI') {
    console.log(`Clicked on ${event.target.textContent}`);
  }
});
Enter fullscreen mode Exit fullscreen mode

Here, a single event listener on <ul> handles clicks for all <li> elements due to event bubbling.

Additional Use Cases

  1. Dynamic Button Creation:

    const buttonContainer = document.getElementById('button-container');
    const addButton = document.getElementById('add-button');
    
    buttonContainer.addEventListener('click', (event) => {
      if (event.target.tagName === 'BUTTON') {
        console.log(`Clicked on ${event.target.textContent}`);
      }
    });
    
    addButton.addEventListener('click', () => {
      const newButton = document.createElement('button');
      newButton.textContent = `Button ${buttonContainer.children.length + 1}`;
      buttonContainer.appendChild(newButton);
    });
    
  2. Streamlining Form Input Handling:

    const userForm = document.getElementById('user-form');
    
    userForm.addEventListener('input', (event) => {
      const { name, value } = event.target;
      console.log(`Changed ${name}: ${value}`);
    });
    

Why It Matters in Interviews

Event delegation is a favorite interview topic because it demonstrates your ability to write efficient and scalable JavaScript. Employers look for developers who can optimize performance and handle dynamic content effectively.

Bonus Insights

  • Always validate the event.target to ensure your logic applies only to the intended elements.
  • Event delegation is not limited to click events; it works for most event types like input, mouseover, or keydown.
  • Avoid unnecessary complexity by choosing the right parent element for attaching the listener.

Learn more about event delegation in JavaScript on GreatFrontEnd

6. How Does this Work in JavaScript?

The this keyword in JavaScript is dynamic, and its value changes based on how a function is invoked. Understanding its behavior is essential for mastering JavaScript's function execution context.

Rules for Determining this

1. Using the new Keyword

When a function is called with new, it creates a new object and sets this to refer to that object.

function Person(name) {
  this.name = name;
}
const person = new Person('Alice');
console.log(person.name); // 'Alice'
Enter fullscreen mode Exit fullscreen mode

2. Explicit Binding with apply, call, or bind

You can explicitly set the value of this using these methods.

function greet() {
  console.log(this.name);
}
const person = { name: 'Alice' };
greet.call(person); // 'Alice'
Enter fullscreen mode Exit fullscreen mode

3. Method Invocation

When a function is called as a method of an object, this refers to the object the method is called on.

const obj = {
  name: 'Alice',
  greet: function () {
    console.log(this.name);
  },
};
obj.greet(); // 'Alice'
Enter fullscreen mode Exit fullscreen mode

4. Free Function Invocation

In non-strict mode, this defaults to the global object (e.g., window in browsers). In strict mode, it defaults to undefined.

function greet() {
  console.log(this); // global object (non-strict) or undefined (strict)
}
greet();
Enter fullscreen mode Exit fullscreen mode

5. Arrow Functions

Arrow functions do not have their own this. Instead, they inherit this from their lexical enclosing scope.

const obj = {
  name: 'Alice',
  greet: () => {
    console.log(this.name); // `this` refers to the surrounding scope
  },
};
obj.greet(); // undefined
Enter fullscreen mode Exit fullscreen mode

Using Arrow Functions with this

Arrow functions simplify working with this, particularly in cases like callbacks. They retain the this value of the enclosing function.

function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++; // `this` refers to the Timer instance
    console.log(this.seconds);
  }, 1000);
}

const timer = new Timer();
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding this is a hallmark of a strong JavaScript developer. Interviewers frequently test this concept because it reveals your understanding of JavaScript's execution context, scope, and function behavior.

Bonus Insights

  • Arrow Functions Simplify Callbacks: Use them for callbacks or methods that need to retain their parent scope’s this.
  • Explicit Binding: Use call or bind for precise control over this.
  • Strict Mode: Always consider how strict mode affects this in free function calls.

Learn more about how this works in JavaScript on GreatFrontEnd

7. What’s the Difference Between Cookies, localStorage, and sessionStorage?

Cookies, localStorage, and sessionStorage are client-side storage mechanisms that serve different purposes in web applications. Understanding their distinctions is key to managing data efficiently.

Cookies

  • Purpose: Designed for server-client communication; stores small pieces of data sent with HTTP requests.
  • Capacity: Limited to ~4KB per domain.
  • Lifespan: Can be session-based (cleared on browser close) or persistent (set with an expiry date).
  • Access: Shared across pages and subdomains of the same origin.
  • Security: Can use HttpOnly and Secure flags to restrict JavaScript access and ensure HTTPS-only transmission.
  • Example Usage:
  // Set a cookie with an expiration date
  document.cookie = 'auth_token=abc123def; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/';

  // Read cookies
  console.log(document.cookie);

  // Delete a cookie
  document.cookie = 'auth_token=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
Enter fullscreen mode Exit fullscreen mode

localStorage

  • Purpose: Stores persistent data in the browser for client-side use.

  • Capacity: Around 5MB per origin.

  • Lifespan: Data persists until explicitly cleared, even after browser restarts.

  • Access: Shared across all tabs and windows of the same origin.

  • Security: Accessible to all JavaScript within the origin; no additional access restrictions.

  • Example Usage:

    // Set an item in localStorage
    localStorage.setItem('key', 'value');
    
    // Retrieve an item from localStorage
    console.log(localStorage.getItem('key'));
    
    // Remove an item from localStorage
    localStorage.removeItem('key');
    
    // Clear all data in localStorage
    localStorage.clear();
    

sessionStorage

  • Purpose: Stores temporary data for the current browser session.

  • Capacity: Similar to localStorage, around 5MB per origin.

  • Lifespan: Data persists only for the lifetime of the current tab or window. Reloading the page retains data, but closing the tab/browser clears it.

  • Access: Limited to the specific tab or window where it was created.

  • Security: Similar to localStorage, accessible to JavaScript within the origin.

  • Example Usage:

    // Set an item in sessionStorage
    sessionStorage.setItem('key', 'value');
    
    // Retrieve an item from sessionStorage
    console.log(sessionStorage.getItem('key'));
    
    // Remove an item from sessionStorage
    sessionStorage.removeItem('key');
    
    // Clear all data in sessionStorage
    sessionStorage.clear();
    

Why It Matters in Interviews

Questions about cookies, localStorage, and sessionStorage evaluate your understanding of client-side storage and how to manage data efficiently in web applications. Knowing the trade-offs of each approach helps you choose the right tool for specific use cases, such as session management or offline data persistence.

Bonus Insights

  • Cookies are essential for managing authentication and sending data to servers.
  • localStorage is ideal for persistent data like user preferences or app settings.
  • sessionStorage is perfect for temporary data that doesn’t need to persist after the session ends.
  • Always consider security implications, such as preventing sensitive data from being exposed to JavaScript.

Discover more about cookies, localStorage, and sessionStorage on GreatFrontEnd

8. What’s the Difference Between <script>, <script async>, and <script defer>?

When embedding JavaScript in an HTML document, the behavior of the <script> tag can vary depending on whether you use async, defer, or no attribute. Each approach impacts how and when the script is loaded and executed.

<script> Tag

  • Behavior: Blocks HTML parsing while the script is fetched and executed. This can delay page rendering.
  • Usage: Use for critical scripts required for immediate functionality, such as inline JavaScript or essential libraries.

Example:

<!doctype html>
<html>
  <head>
    <title>Regular Script</title>
  </head>
  <body>
    <h1>Regular Script Example</h1>
    <p>This content appears before the script executes.</p>

    <script src="regular.js"></script>

    <p>This content appears after the script executes.</p>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

<script async> Tag

  • Behavior: Downloads the script in parallel with HTML parsing and executes it as soon as it's available. The execution order may vary.
  • Usage: Ideal for independent scripts like analytics or ads that don't rely on other scripts or the DOM.

Example:

<!doctype html>
<html>
  <head>
    <title>Async Script</title>
  </head>
  <body>
    <h1>Async Script Example</h1>
    <p>This content appears before the async script executes.</p>

    <script async src="async.js"></script>

    <p>This content may appear before or after the async script executes.</p>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

<script defer> Tag

  • Behavior: Downloads the script alongside HTML parsing but delays execution until the document is fully parsed. Execution order is preserved for multiple deferred scripts.
  • Usage: Perfect for scripts that rely on the DOM being fully loaded.

Example:

<!doctype html>
<html>
  <head>
    <title>Deferred Script</title>
  </head>
  <body>
    <h1>Deferred Script Example</h1>
    <p>This content appears before the deferred script executes.</p>

    <script defer src="deferred.js"></script>

    <p>This content appears before the deferred script executes.</p>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding these attributes highlights your ability to optimize website performance and prioritize scripts effectively. Interviewers may test your knowledge of how script loading impacts user experience and page speed.

Bonus Insights

  • Use defer for scripts that manipulate the DOM to ensure it is fully parsed.
  • Use async for scripts that don't depend on other resources or DOM elements.
  • Avoid blocking scripts with plain <script> tags unless necessary.

Learn more about the difference between <script>, <script async>, and <script defer> on GreatFrontEnd

9. What’s the Difference Between null, undefined, and Undeclared Variables?

JavaScript variables can exist in several states: null, undefined, or undeclared. Knowing these differences is crucial for managing and debugging your code.

Undeclared

  • Definition: Variables that are not declared using var, let, or const. Assigning a value to an undeclared variable creates a global variable (non-strict mode).
  • Issues: Can lead to hard-to-debug errors and conflicts in global scope.
  • Detection: Use try/catch or static analysis tools like ESLint to avoid undeclared variables.

undefined

  • Definition: Indicates a declared variable without an assigned value.
  • Behavior: Automatically assigned to variables that are declared but not initialized.
  • Checks: Use typeof or === to check for undefined. Avoid == as it returns true for both undefined and null.

null

  • Definition: A deliberate assignment representing "no value."
  • Behavior: Used to reset or clear variables.
  • Checks: Use === to differentiate between null and undefined. Avoid == as it treats both as equivalent.

Best Practices

  1. Always Declare Variables:

    let declaredVar;
    console.log(declaredVar); // undefined
    
  2. Assign null When Needed:

    let data = null; // Explicitly clearing the variable
    
  3. Use Static Tools: Linting tools like ESLint or TypeScript help catch undeclared variables before runtime.

Why It Matters in Interviews

Understanding these states shows your grasp of variable initialization, error prevention, and debugging in JavaScript. It's a foundational concept that interviewers expect you to explain with confidence.

Bonus Insights

  • Avoid undeclared variables entirely by always using let or const.
  • Use undefined for system-generated "missing values" and null for developer-intended "no value" states.
  • Rely on static analysis tools to catch potential issues during development.

Learn more about null, undefined, and undeclared variables on GreatFrontEnd

10. What’s the Difference Between .call and .apply in JavaScript?

.call and .apply are methods used to invoke functions while explicitly setting the value of this inside the function. The key difference lies in how they handle arguments:

  • .call: Accepts arguments as a comma-separated list.
  • .apply: Accepts arguments as an array.

Memory Aid:

  • C for call and comma-separated.
  • A for apply and array.

Example:

function add(a, b) {
  return a + b;
}

console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3

// Using ES6 spread operator with .call
console.log(add.call(null, ...[1, 2])); // 3
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Demonstrating the use of .call and .apply shows your understanding of function invocation and context manipulation in JavaScript. This knowledge is critical for tasks like borrowing methods or working with variable-length arguments.

Bonus Insights

  • .call is generally more concise for known argument counts, while .apply is useful for dynamic argument arrays.
  • The spread operator (...) in ES6 allows .call to mimic .apply.

Learn more about .call and .apply in JavaScript on GreatFrontEnd

11. What Does Function.prototype.bind Do in JavaScript?

The bind method creates a new function that has a specific this value and optional preset arguments. It’s particularly useful for maintaining the correct this context when passing functions as callbacks.

Example:

const john = {
  age: 42,
  getAge: function () {
    return this.age;
  },
};

console.log(john.getAge()); // 42

const unboundGetAge = john.getAge;
console.log(unboundGetAge()); // undefined

const boundGetAge = john.getAge.bind(john);
console.log(boundGetAge()); // 42

const mary = { age: 21 };
const boundGetAgeMary = john.getAge.bind(mary);
console.log(boundGetAgeMary()); // 21
Enter fullscreen mode Exit fullscreen mode

Key Uses of bind

  1. Preserving this Context:

    bind ensures the function always uses the specified this value, even when passed around as a callback.

  2. Partial Application of Arguments:

    bind can pre-set arguments, making it a lightweight tool for creating partially applied functions.

    function multiply(a, b) {
      return a * b;
    }
    
    const double = multiply.bind(null, 2);
    console.log(double(4)); // 8
    
  3. Method Borrowing:

    Use bind to borrow methods from one object and apply them to another.

    const array = [1, 2, 3];
    const printLength = Array.prototype.length.bind(array);
    console.log(printLength()); // 3
    

Why It Matters in Interviews

Understanding bind demonstrates a deep grasp of function context in JavaScript. It’s a key skill when working with event handlers, callbacks, or libraries that rely on this.

Bonus Insights

  • bind doesn’t execute the function immediately but returns a new bound function.
  • Combine bind with setTimeout or event handlers to avoid unexpected this values.

Learn more about Function.prototype.bind on GreatFrontEnd

12. Why Use Arrow Syntax for Methods in a Constructor?

Using the arrow syntax for methods in a constructor ensures that the this value is always bound to the instance of the constructor. This eliminates issues caused by changing contexts, making the code more predictable and easier to maintain.

Key Advantages

  1. Automatic Binding: Arrow functions inherit this from their surrounding lexical scope, avoiding the need to manually bind this in the constructor.
  2. Consistency: The this context remains tied to the object instance, regardless of how the method is called.

Example:

const Person = function (name) {
  this.name = name;
  this.sayName1 = function () {
    console.log(this.name);
  };
  this.sayName2 = () => {
    console.log(this.name);
  };
};

const john = new Person('John');
const dave = new Person('Dave');

john.sayName1(); // John
john.sayName2(); // John

// `this` can change for regular functions but not for arrow functions
john.sayName1.call(dave); // Dave
john.sayName2.call(dave); // John
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • sayName1 is a regular function, so its this value changes depending on how it’s called.
  • sayName2 is an arrow function, so its this remains bound to the john instance.

This is particularly useful in frameworks like React, where arrow functions simplify passing methods to child components without the need for explicit binding.

Why It Matters in Interviews

Understanding how arrow functions handle this demonstrates your grasp of JavaScript’s execution context. It also showcases your ability to write cleaner, more maintainable code in scenarios like object-oriented design or React development.

Bonus Insights

  • Avoid using arrow functions as methods in prototypes or objects since they lack their own this.
  • Arrow functions are perfect for callbacks or methods that need consistent this behavior.

Learn more about the advantages of arrow syntax in constructors on GreatFrontEnd

13. How Does Prototypal Inheritance Work?

Prototypal inheritance enables objects to share properties and methods by linking them through a prototype. It’s a foundational concept of JavaScript’s object-oriented programming.

Key Concepts

1. Prototypes

Every object has a prototype, an object it inherits from. You can access or modify prototypes using:

  • Object.getPrototypeOf()
  • Object.setPrototypeOf()

Example:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function () {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

let john = new Person('John', 30);
john.sayHello(); // "Hello, my name is John and I am 30 years old."
Enter fullscreen mode Exit fullscreen mode

2. Prototype Chain

When you access a property or method, JavaScript searches the object first. If it’s not found, the search continues up the prototype chain until it reaches null.

3. Constructor Functions

Constructor functions use new to create objects and set their prototype to the constructor's prototype.

Example:

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

Animal.prototype.sayName = function () {
  console.log(`My name is ${this.name}`);
};

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.bark = function () {
  console.log('Woof!');
};

let fido = new Dog('Fido', 'Labrador');
fido.bark(); // "Woof!"
fido.sayName(); // "My name is Fido"
Enter fullscreen mode Exit fullscreen mode

4. Object.create()

This method creates a new object with a specified prototype.

Example:

let proto = {
  greet: function () {
    console.log(`Hello, my name is ${this.name}`);
  },
};

let person = Object.create(proto);
person.name = 'John';
person.greet(); // "Hello, my name is John"
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Prototypal inheritance is a core concept of JavaScript, reflecting how objects share behavior and methods. Interviewers often use this topic to test your understanding of object-oriented programming and how JavaScript differs from class-based languages.

Bonus Insights

  • ES6 classes are syntactic sugar over prototypal inheritance. Understanding the underlying prototype model is essential.
  • Avoid directly modifying the prototype of native objects (like Array.prototype) to prevent unintended side effects.

Learn more about prototypal inheritance on GreatFrontEnd

14. What’s the Difference Between function Person(){}, const person = Person(), and const person = new Person()?

Understanding the nuances between these statements helps clarify how JavaScript handles function calls and object creation.

1. Function Declaration

function Person(){} is a basic function declaration. When written in PascalCase, it follows the convention for functions intended to act as constructors.

2. Function Call

const person = Person() calls the function and executes its code. If the function doesn’t explicitly return a value, person will be undefined. This is not a constructor call and doesn’t create a new object.

3. Constructor Call

const person = new Person() invokes the Person function as a constructor. The new keyword creates a new object, sets its prototype to Person.prototype, and binds this to the new object.

Why It Matters in Interviews

Understanding the difference between function calls and constructor calls demonstrates your grasp of object creation and JavaScript's prototypal inheritance. These are essential concepts for any frontend developer.

Bonus Insights

  • Always use the new keyword for constructor functions to avoid unexpected behavior.
  • ES6 classes are syntactic sugar over the constructor function pattern.

Learn more about the difference between function Person(){}, const person = Person(), and const person = new Person() on GreatFrontEnd

15. Function Declarations vs. Function Expressions

The way you define a function impacts how it behaves and where it’s accessible. Let’s break it down:

Function Declarations

  • Syntax: function foo() {}
  • Behavior: Defines a named function that’s hoisted, allowing it to be used before its definition.

Example:

foo(); // "FOOOOO"

function foo() {
  console.log('FOOOOO');
}
Enter fullscreen mode Exit fullscreen mode

Function Expressions

  • Syntax: var foo = function() {}
  • Behavior: Creates an unnamed (or optionally named) function assigned to a variable. Only the variable declaration is hoisted, not the function definition.

Example:

foo(); // Uncaught TypeError: foo is not a function

var foo = function () {
  console.log('FOOOOO');
};
Enter fullscreen mode Exit fullscreen mode

Key Differences

  1. Hoisting:
    • Function declarations are fully hoisted, making them callable before their definition.
    • Function expressions are partially hoisted (variable only).
  2. Scope:
    • Function expressions can use internal names for recursion or debugging, but these names are not accessible outside the function.

Why It Matters in Interviews

This topic tests your understanding of function scope, hoisting, and variable declarations—key areas for writing clean, maintanble JavaScript.

Bonus Insights

  • Use function declarations for reusable, top-level functions.
  • Use function expressions for inline or contextual logic, such as callbacks.

Learn more about function declarations and expressions on GreatFrontEnd

16. What Are the Different Ways to Create Objects in JavaScript?

JavaScript offers multiple ways to create objects, catering to different use cases. Here are the most common methods:

1. Object Literals

The simplest and most common way to create objects using key-value pairs.

const person = {
  firstName: 'John',
  lastName: 'Doe',
};
Enter fullscreen mode Exit fullscreen mode

2. Object() Constructor

Use the Object constructor with the new keyword.

const person = new Object();
person.firstName = 'John';
person.lastName = 'Doe';
Enter fullscreen mode Exit fullscreen mode

3. Object.create()

Creates a new object using an existing object as its prototype.

const personPrototype = {
  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  },
};

const person = Object.create(personPrototype);
person.name = 'John';
person.age = 30;

person.greet(); // Output: Hello, my name is John and I'm 30 years old.
Enter fullscreen mode Exit fullscreen mode

4. ES6 Classes

Provides a modern syntax for creating object blueprints.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  }
}

const person = new Person('John', 30);
person.greet(); // Output: Hello, my name is John and I'm 30 years old.
Enter fullscreen mode Exit fullscreen mode

5. Constructor Functions

Similar to classes, these use the new keyword to create objects.

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.greet = function () {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  };
}

const person = new Person('John', 30);
person.greet(); // Output: Hello, my name is John and I'm 30 years old.
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Knowing these techniques highlights your understanding of JavaScript’s flexibility and object-oriented principles. It also showcases your ability to choose the right approach for specific scenarios.

Bonus Insights

  • Object literals are best for simple, static objects.
  • Classes provide cleaner, more structured syntax for creating multiple instances.
  • Use Object.create() for prototypal inheritance.

Explore the various ways to create objects in JavaScript on GreatFrontEnd

17. What Is a Higher-Order Function in JavaScript?

A higher-order function is a function that interacts with other functions in one of two ways:

  1. Accepts a function as an argument:

    This allows you to pass logic dynamically to a higher-order function.

      function greet(name) {
        return `Hello, ${name}!`;
      }
    
      function greetName(greeter, name) {
        console.log(greeter(name));
      }
    
      greetName(greet, 'Alice'); // Output: Hello, Alice!
    
  2. Returns a function:

    This enables function creation on the fly, often used for closures or reusable logic.

    function multiplier(factor) {
     return function (num) {
       return num * factor;
     };
    }
    
    const double = multiplier(2);
    console.log(double(5)); // Output: 10
    

Higher-order functions are a cornerstone of functional programming, enhancing code modularity and reusability.

Why It Matters in Interviews

Knowledge of higher-order functions showcases your understanding of JavaScript's functional programming aspects. It's a common topic in interviews, especially for roles requiring framework expertise.

Bonus Insights

  • Examples like map, reduce, and filter are real-world applications of higher-order functions.
  • Understanding closures is often necessary when dealing with higher-order functions.

Learn more about higher-order functions on GreatFrontEnd

18. How Do ES2015 Classes Compare to ES5 Function Constructors?

JavaScript offers two main ways to create objects: ES5 function constructors and ES2015 (ES6) classes. They achieve similar goals but differ in usability and readability.

ES5 Function Constructors

  • Based on constructor functions and prototypes for object creation.
  • Example:

    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    
    Person.prototype.greet = function () {
      console.log(
        'Hello, my name is ' + this.name + ' and I am ' + this.age + ' years old.'
      );
    };
    
    var person1 = new Person('John', 30);
    person1.greet(); // Hello, my name is John and I am 30 years old.
    

ES2015 Classes

  • Introduces the class syntax, offering better readability and added features.
  • Example:

    class Person {
      constructor(name, age) {
        this.name = name;
        this.age = age;
      }
    
      greet() {
        console.log(
          `Hello, my name is ${this.name} and I am ${this.age} years old.`
        );
      }
    }
    
    const person1 = new Person('John', 30);
    person1.greet(); // Hello, my name is John and I am 30 years old.
    

Key Differences

  1. Syntax and Readability:
    • ES5: Relies on manual prototype chaining and function constructors.
    • ES2015: Uses the intuitive class and extends keywords.
  2. Static Methods:
    • ES5: Added directly to the constructor function.
    • ES2015: Declared using the static keyword inside the class.
  3. Inheritance:
    • ES5: Achieved using Object.create() and manual prototype management.
    • ES2015: Simplified with the extends keyword.
  4. Super Calls:
    • ES5: Manual calls to parent constructors.
    • ES2015: The super keyword makes parent class interactions more straightforward.

Why It Matters in Interviews

Familiarity with both approaches shows you can handle modern and legacy codebases. Many interview questions focus on ES2015 classes as they are now the standard.

Bonus Insights

  • ES2015 classes are syntactic sugar over ES5 prototypes but much easier to use.
  • Stick to classes for modern projects to write more concise and maintainable code.

Explore ES2015 classes vs. ES5 constructors on GreatFrontEnd

19. What Is Event Bubbling in JavaScript?

Event bubbling is a process in the DOM where an event starts at the target element and propagates upward through its ancestors.

Bubbling Phase

  • Description: Events, such as click, trigger on the target element and then bubble up through its parent elements, eventually reaching the root of the DOM tree.
  • Example:

    // HTML:
    // <div id="parent">
    //   <button id="child">Click me!</button>
    // </div>
    const parent = document.getElementById('parent');
    const child = document.getElementById('child');
    
    parent.addEventListener('click', () => {
      console.log('Parent element clicked');
    });
    
    child.addEventListener('click', () => {
      console.log('Child element clicked');
    });
    

    Clicking the button triggers both the child and parent event handlers due to bubbling.

Stopping Event Bubbling

Use stopPropagation() to prevent the event from moving up the DOM tree.

Example:

child.addEventListener('click', (event) => {
  console.log('Child element clicked');
  event.stopPropagation();
});
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Event bubbling is a fundamental concept for handling DOM events efficiently. It’s often asked alongside event delegation to test your understanding of JavaScript’s event model.

Bonus Insights

  • Combine event bubbling with delegation for dynamic elements.
  • Use stopPropagation() sparingly to avoid breaking other event listeners.

Learn more about event bubbling on GreatFrontEnd

20. What Is Event Capturing in JavaScript?

Event capturing is a DOM event propagation mechanism where an event starts at the root of the document and flows down through the DOM tree to the target element.

Event Propagation Phases

  1. Capturing Phase: The event moves from the root element down to the target element.
  2. Target Phase: The event reaches the target element.
  3. Bubbling Phase: The event propagates back up from the target element to the root.

Enabling Event Capturing

Event capturing is disabled by default but can be enabled by passing { capture: true } as the third argument to addEventListener().

Example:

// HTML:
// <div id="parent">
//   <button id="child">Click me!</button>
// </div>
const parent = document.getElementById('parent');
const child = document.getElementById('child');

parent.addEventListener(
  'click',
  () => {
    console.log('Parent element clicked (capturing)');
  },
  true // Enable capturing phase
);

child.addEventListener('click', () => {
  console.log('Child element clicked');
});
Enter fullscreen mode Exit fullscreen mode

Clicking the button triggers the parent's capturing handler first, followed by the child's event handler.

Stopping Propagation in Capturing Phase

Use stopPropagation() to prevent the event from propagating further during the capturing phase.

Example:

parent.addEventListener(
  'click',
  (event) => {
    console.log('Parent element clicked (capturing)');
    event.stopPropagation(); // Stop propagation
  },
  true
);

child.addEventListener('click', () => {
  console.log('Child element clicked');
});
Enter fullscreen mode Exit fullscreen mode

In this case, the parent's capturing handler is executed, but the child's handler is not triggered due to stopPropagation().

Why It Matters in Interviews

Understanding event capturing is crucial for handling events in complex DOM structures. It pairs well with event delegation and demonstrates your grasp of JavaScript event propagation.

Bonus Insights

  • Event capturing is the opposite of event bubbling, where events propagate upward.
  • Use capturing for scenarios where parent elements need priority handling.

Learn more about event capturing on GreatFrontEnd

21. How Do mouseenter and mouseover Differ in JavaScript?

While both mouseenter and mouseover detect when the mouse pointer enters an element, they differ in behavior:

mouseenter

  • Does Not Bubble: The event is only triggered on the element itself.
  • No Trigger for Descendants: It does not activate when moving over child elements.
  • Use Case: Ideal for handling hover events specific to the parent element without interference from children.

mouseover

  • Bubbles Up: The event propagates up the DOM tree.
  • Triggered on Descendants: Fires multiple times if the mouse moves over child elements.
  • Use Case: Useful for detecting hover events across an element and its children.

Why It Matters in Interviews

This question tests your understanding of DOM events and event bubbling. Knowing the difference is essential for implementing efficient hover effects and avoiding redundant event handling.

Bonus Insights

  • Use mouseenter for simpler hover logic and mouseover when descendant interactions are required.
  • Combine mouseenter with mouseleave for a clean hover experience.

Explore the difference between mouseenter and mouseover on GreatFrontEnd

22. Synchronous vs. Asynchronous Functions in JavaScript

JavaScript functions can execute either synchronously or asynchronously, depending on their behavior and use case.

Synchronous Functions

  • Execution: Run one after the other in a sequential order.
  • Blocking: Halts program execution until the current task is completed.
  • Predictable Flow: Easier to debug due to their strict order.
  • Examples: Reading files synchronously, large data loops.

Example:

const fs = require('fs');
const data = fs.readFileSync('large-file.txt', 'utf8');
console.log(data); // Blocks until file is read
console.log('End of the program');
Enter fullscreen mode Exit fullscreen mode

Asynchronous Functions

  • Execution: Do not block the program; other tasks can run concurrently.
  • Non-Blocking: Improves responsiveness and performance.
  • Common Use Cases: Network requests, file I/O, event handling, and animations.

Example:

console.log('Start of the program');

fetch('https://api.example.com/data')
  .then((response) => response.json())
  .then((data) => console.log(data)) // Non-blocking
  .catch((error) => console.error(error));

console.log('End of program');
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding synchronous vs. asynchronous behavior is essential for writing efficient JavaScript. Many interview scenarios involve explaining how JavaScript handles asynchronous tasks like promises, callbacks, and the event loop.

Bonus Insights

  • Use synchronous functions sparingly in Node.js to avoid blocking the event loop.
  • Asynchronous patterns like async/await provide cleaner syntax for handling complex workflows.

Learn more about the difference between synchronous and asynchronous functions on GreatFrontEnd

23. What Is AJAX and How Does It Work?

AJAX (Asynchronous JavaScript and XML) enables web applications to send and retrieve data from a server asynchronously, allowing dynamic page updates without a full reload. This creates a smoother user experience by handling data changes in real-time.

Key Features of AJAX

  • Asynchronous: Allows parts of a web page to update without reloading the entire page.
  • Data Formats: While it originally used XML, JSON is now the standard for its simplicity and compatibility.
  • APIs: Traditionally relied on XMLHttpRequest, but fetch() is now the modern alternative.

Using XMLHttpRequest

Example:

let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
  if (xhr.readyState === XMLHttpRequest.DONE) {
    if (xhr.status === 200) {
      console.log(xhr.responseText);
    } else {
      console.error('Request failed: ' + xhr.status);
    }
  }
};
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);
xhr.send();
Enter fullscreen mode Exit fullscreen mode
  • Flow: Create an XMLHttpRequest object, set up a callback for state changes, open a connection, and send the request.

Using fetch()

Example:

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then((response) => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then((data) => console.log(data))
  .catch((error) => console.error('Fetch error:', error));
Enter fullscreen mode Exit fullscreen mode
  • Flow: Use fetch() to send a request, handle the response with .then() to parse JSON data, and catch errors with .catch().

How AJAX Works with fetch()

  1. Making a Request: Initiate a request with the desired method and headers.

    fetch('https://api.example.com/data', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      },
    });
    
  2. Promises: fetch() returns a Promise that resolves to a Response object.

  3. Handling the Response: Use .json() or .text() to parse the response body.

  4. Asynchronous Execution: The browser processes other tasks while waiting for the server response.

  5. Error Handling: Handle errors via .catch() or try/catch with async/await.

Why It Matters in Interviews

AJAX demonstrates your ability to create dynamic, responsive web applications. Understanding its use with modern tools like fetch() highlights your proficiency in handling asynchronous JavaScript.

Bonus Insights

  • Use fetch() for cleaner, promise-based requests.
  • Employ async/await for improved readability in asynchronous workflows.

Explore AJAX in detail on GreatFrontEnd

24. What Are the Pros and Cons of Using AJAX?

AJAX allows dynamic web page updates without full reloads, but it comes with trade-offs.

Advantages of AJAX

  • Dynamic Updates: Refreshes only necessary parts of a page, improving performance.
  • User Experience: Provides a seamless, responsive interface.
  • Reduced Server Load: Optimizes resources by minimizing unnecessary data transfer.

Disadvantages of AJAX

  • JavaScript Dependency: Breaks functionality if JavaScript is disabled.
  • SEO Challenges: Dynamic content is harder for search engines to index.
  • Performance Issues: Can strain low-end devices when handling large datasets.
  • Bookmarking: Difficult to maintain specific states for browser navigation.

Why It Matters in Interviews

Understanding AJAX highlights your ability to build interactive, user-friendly web applications while managing its limitations effectively.

Bonus Insights

  • Combine AJAX with frameworks like React for optimal dynamic content handling.
  • Leverage server-side rendering or SEO-friendly solutions to address indexing challenges.

Learn more about the pros and cons of AJAX on GreatFrontEnd

25. How Do XMLHttpRequest and fetch() Differ?

Both XMLHttpRequest and fetch() handle asynchronous HTTP requests, but they differ significantly in their design and usage.

Key Differences

  1. Syntax:
    • XMLHttpRequest: Event-driven with callbacks.
    • fetch(): Promise-based, offering cleaner syntax.
  2. Headers:
    • XMLHttpRequest: Use setRequestHeader for configuration.
    • fetch(): Pass headers as an object in the request options.
  3. Request Body:
    • XMLHttpRequest: Add using send() method.
    • fetch(): Include via body property in the options.
  4. Response Handling:
    • XMLHttpRequest: Use responseType for formats like JSON or Blob.
    • fetch(): Use .json() or .text() methods.
  5. Error Handling:
    • XMLHttpRequest: Relies on onerror event.
    • fetch(): Uses .catch() for cleaner error management.
  6. Progress Tracking:
    • XMLHttpRequest: Supports onprogress for monitoring uploads/downloads.
    • fetch(): Does not support native progress tracking.
  7. Cancellation:
    • XMLHttpRequest: Use abort() to stop requests.
    • fetch(): Use AbortController for managing cancellations.

Why It Matters in Interviews

This question tests your understanding of modern JavaScript tools versus legacy methods. Demonstrating the pros and cons of each shows your ability to choose the right tool for specific scenarios.

Bonus Insights

  • Prefer fetch() for its simplicity and modern syntax.
  • Use XMLHttpRequest for scenarios requiring progress tracking or older browser compatibility.

Learn more about the differences between XMLHttpRequest and fetch() on GreatFrontEnd

26. What Are the Different Data Types in JavaScript?

JavaScript supports various data types, categorized as primitive or non-primitive (reference) types.

Primitive Data Types

  1. Number: Represents integers and floating-point numbers.
  2. String: Text data enclosed in single, double quotes, or backticks.
  3. Boolean: Logical values true or false.
  4. Undefined: A variable declared but not assigned a value.
  5. Null: Represents an intentional absence of value.
  6. Symbol: Unique and immutable values, often used as object keys.
  7. BigInt: Represents integers of arbitrary precision, useful for very large numbers.

Non-Primitive Data Types

  1. Object: Stores collections of data and more complex entities.
  2. Array: Ordered list of data.
  3. Function: JavaScript functions are objects and can be passed around.
  4. Date: Represents dates and times.
  5. RegExp: Regular expressions for pattern matching in strings.
  6. Map: Key-value pairs with keys of any type.
  7. Set: Stores unique values of any type.

Determining Data Types

JavaScript is dynamically typed, allowing variables to hold different types. Use typeof to check a variable's type.

Why It Matters in Interviews

Understanding data types is fundamental to mastering JavaScript. Questions on types often serve as a gateway to topics like type coercion, object prototypes, and memory management.

Bonus Insights

  • Learn the differences between null and undefined to handle edge cases better.
  • Use BigInt for applications needing high-precision arithmetic.

Explore the various data types in JavaScript on GreatFrontEnd

27. How Do You Iterate Over Objects and Arrays in JavaScript?

Iterating over object properties and arrays is a common task in JavaScript. Here’s how you can achieve this:

Iterating Over Objects

  1. for...in Statement

    Loops over enumerable properties, including inherited ones.

    for (const property in obj) {
      if (Object.hasOwn(obj, property)) {
        console.log(property);
      }
    }
    
  2. Object.keys()

    Retrieves an array of the object’s own enumerable properties.

    Object.keys(obj).forEach((property) => console.log(property));
    
  3. Object.entries()

    Provides an array of key-value pairs.

    Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`));
    
  4. Object.getOwnPropertyNames()

    Retrieves all properties, including non-enumerable ones.

    Object.getOwnPropertyNames(obj).forEach((property) => console.log(property));
    

Iterating Over Arrays

  1. for Loop

    Classic iteration using an index.

    for (let i = 0; i < arr.length; i++) {
      console.log(arr[i]);
    }
    
  2. Array.prototype.forEach()

    Executes a callback for each element.

    arr.forEach((element, index) => console.log(element, index));
    
  3. for...of Statement

    Iterates over iterable objects like arrays.

    for (let element of arr) {
      console.log(element);
    }
    
  4. Array.prototype.entries()

    Provides index-value pairs for iteration.

    for (let [index, elem] of arr.entries()) {
      console.log(index, ':', elem);
    }
    

Why It Matters in Interviews

Iteration techniques reveal your understanding of JavaScript’s built-in constructs and their efficiency in different scenarios.

Bonus Insights

  • Use for...in cautiously, as it traverses inherited properties.
  • For modern codebases, prefer for...of or array methods for cleaner syntax.

Explore language constructions for iterating over objects and arrays on GreatFrontEnd

28. What Are the Benefits of Spread Syntax vs. Rest Syntax?

The spread (...) and rest (...) syntaxes are versatile tools in JavaScript, introduced in ES2015.

Spread Syntax (...)

Used to unpack elements from arrays or objects.

  1. Copying Arrays/Objects

    const array = [1, 2, 3];
    const newArray = [...array]; // [1, 2, 3]
    
  2. Merging Arrays/Objects

    const arr1 = [1, 2];
    const arr2 = [3, 4];
    const mergedArray = [...arr1, ...arr2]; // [1, 2, 3, 4]
    
  3. Passing Arguments

    const nums = [1, 2, 3];
    Math.max(...nums); // Same as Math.max(1, 2, 3)
    

Rest Syntax (...)

Gathers multiple elements into an array or object.

  1. Function Parameters

    function sum(...numbers) {
      return numbers.reduce((a, b) => a + b, 0);
    }
    console.log(sum(1, 2, 3)); // 6
    
  2. Destructuring Arrays

    const [first, ...rest] = [1, 2, 3];
    console.log(first); // 1
    console.log(rest); // [2, 3]
    
  3. Destructuring Objects

    const { name, ...details } = { name: 'Alice', age: 25, city: 'NY' };
    console.log(details); // { age: 25, city: 'NY' }
    

Key Differences

  • Spread: Expands elements into a list.
  • Rest: Collects elements into an array or object.

Why It Matters in Interviews

Mastery of spread and rest syntax demonstrates your ability to write concise and modern JavaScript code, a skill highly valued in frameworks like React and Redux.

Bonus Insights

  • Spread syntax creates shallow copies; use caution with nested structures.
  • Rest syntax simplifies handling variable-length arguments.

Discover the benefits of spread and rest syntax on GreatFrontEnd

29. How Do Map and Plain Objects Differ in JavaScript?

JavaScript offers both Map and plain objects for storing key-value pairs, but they differ in several aspects:

Key Differences

  1. Key Types:

    • Map: Allows keys of any type, including objects and functions.
    • Plain Objects: Keys are strings or symbols. Non-string keys are converted to strings.
  2. Order:

    • Map: Maintains the insertion order of keys.
    • Plain Objects: Order is not guaranteed for non-integer keys.
  3. Size:

    • Map: Has a size property to get the number of key-value pairs.
    • Plain Objects: No built-in property; must count keys manually.
  4. Iteration:

    • Map: Directly iterable using forEach, keys(), values(), or entries().
    • Plain Objects: Requires Object.keys(), Object.values(), or Object.entries() for iteration.
  5. Performance:

    • Map: Optimized for frequent additions and deletions.
    • Plain Objects: Faster for smaller datasets and simple operations.

Example

// Map Example
const map = new Map();
map.set('key1', 'value1');
map.set({ key: 'key2' }, 'value2');
console.log(map.size); // 2

// Plain Object Example
const obj = { key1: 'value1' };
obj[{ key: 'key2' }] = 'value2';
console.log(Object.keys(obj).length); // 1 (keys are strings)
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding when to use Map over plain objects shows your knowledge of JavaScript’s data structures and their performance implications.

Bonus Insights

  • Use Map for dynamic datasets and keys of various types.
  • Use plain objects for simple, static data or configuration purposes.

Explore the difference between Map and plain objects on GreatFrontEnd

30. How Do Map/Set and WeakMap/WeakSet Differ?

Map/Set and WeakMap/WeakSet are similar data structures but differ in functionality and use cases:

Key Differences

  1. Key Types:
    • Map/Set: Accept any type as keys, including primitives.
    • WeakMap/WeakSet: Only accept objects as keys.
  2. Memory Management:
    • Map/Set: Maintain strong references, preventing garbage collection.
    • WeakMap/WeakSet: Use weak references, allowing objects to be garbage collected if no other references exist.
  3. Key Enumeration:
    • Map/Set: Iterable; keys and values can be retrieved.
    • WeakMap/WeakSet: Non-enumerable; no direct way to retrieve keys or values.
  4. Size Property:
    • Map/Set: Provide a size property.
    • WeakMap/WeakSet: No size property, as they depend on garbage collection.
  5. Use Cases:
    • Map/Set: Suitable for general-purpose data storage and caching.
    • WeakMap/WeakSet: Ideal for associating metadata with objects without preventing garbage collection.

Why It Matters in Interviews

Distinguishing these structures showcases your understanding of JavaScript memory management and optimal data storage practices.

Bonus Insights

  • Use WeakMap for private data in classes or metadata storage.
  • Opt for Map/Set when enumeration or explicit key management is required.

Explore the differences between Map/Set and WeakMap/WeakSet on GreatFrontEnd

31. What Is a Practical Use Case for Arrow Functions?

Arrow functions simplify callbacks and inline function definitions, making code cleaner and more concise.

Use Case: Array Mapping

Suppose you have an array of numbers and want to double each element using map().

Traditional Function Syntax:

const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map(function (number) {
  return number * 2;
});

console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
Enter fullscreen mode Exit fullscreen mode

Arrow Function Syntax:

const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((number) => number * 2);

console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]
Enter fullscreen mode Exit fullscreen mode

The arrow function provides a cleaner, more modern way to express the same logic.

Why It Matters in Interviews

Arrow functions are a core part of modern JavaScript. Demonstrating their use showcases your ability to write concise, maintainable code.

Bonus Insights

  • Arrow functions do not have their own this context, making them ideal for callback scenarios.
  • Use them for short functions; stick to traditional functions for complex logic or when this binding is required.

Discover a use case for arrow functions on GreatFrontEnd

32. What Are Callback Functions in Asynchronous Operations?

In JavaScript, a callback function is passed as an argument to another function and is executed after the completion of an asynchronous task. This pattern is commonly used in operations like fetching data or handling I/O.

Example: Fetching Data with a Callback

function fetchData(callback) {
  setTimeout(() => {
    const data = { name: 'John', age: 30 };
    callback(data);
  }, 1000);
}

fetchData((data) => {
  console.log(data); // { name: 'John', age: 30 }
});
Enter fullscreen mode Exit fullscreen mode

Here, the fetchData function takes a callback as an argument. Once the data is ready, the callback is invoked to process the data.

Why It Matters in Interviews

Understanding callback functions demonstrates your knowledge of asynchronous programming, a critical skill in modern JavaScript development.

Bonus Insights

  • Callbacks can lead to "callback hell" in deeply nested scenarios. Modern alternatives like promises and async/await make code more readable.
  • Use named functions as callbacks to improve code clarity and maintainability.

Learn more about the concept of callback functions in asynchronous operations on GreatFrontEnd

33. What Are Debouncing and Throttling?

Both debouncing and throttling are techniques to control the frequency of function execution, optimizing performance in event-driven JavaScript applications.

Debouncing

Delays the execution of a function until after a specified time has elapsed since its last call. Useful for tasks like handling search inputs.

Example:

function debounce(func, delay) {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

// Usage
const processChange = debounce(() => console.log('Input processed'), 300);
document.getElementById('search').addEventListener('input', processChange);
Enter fullscreen mode Exit fullscreen mode

Throttling

Limits the execution of a function to at most once in a specified interval. Useful for frequent events like window resizing or scrolling.

Example:

function throttle(func, limit) {
  let inThrottle;
  return (...args) => {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

// Usage
const logScroll = throttle(() => console.log('Scrolling'), 500);
window.addEventListener('scroll', logScroll);
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Debouncing and throttling questions test your understanding of performance optimization and event handling in JavaScript.

Bonus Insights

  • Use debouncing for user-input-driven events like search boxes.
  • Throttle high-frequency events like scroll or resize to enhance app responsiveness.

Discover the concept of debouncing and throttling on GreatFrontEnd

34. What Is Destructuring Assignment in JavaScript?

Destructuring assignment simplifies extracting values from arrays or objects into individual variables. This concise syntax improves readability and reduces boilerplate code.

Examples of Destructuring

  1. Array Destructuring:

    Assign array elements to variables using square brackets.

    const [a, b] = [1, 2];
    console.log(a); // 1
    console.log(b); // 2
    
  2. Object Destructuring:

    Extract object properties using curly braces.

    const { name, age } = { name: 'John', age: 30 };
    console.log(name); // John
    console.log(age); // 30
    

Why It Matters in Interviews

Destructuring showcases your ability to write clean, modern JavaScript. It’s frequently used in frameworks like React for props and state handling.

Bonus Insights

  • Combine destructuring with default values for robust code:

    const { name = 'Guest' } = {};
    console.log(name); // Guest
    
  • Use destructuring in function parameters for cleaner APIs:

    function greet({ name, age }) {
      console.log(`Hello ${name}, you are ${age}`);
    }
    greet({ name: 'John', age: 30 });
    

Discover the concept of destructuring assignment for objects and arrays on GreatFrontEnd

35. How Does Hoisting Apply to Functions?

In JavaScript, hoisting allows function declarations to be accessed before their actual definition in the code. However, function expressions and arrow functions must be defined before they are used to avoid runtime errors.

Example: Hoisted Function vs. Non-Hoisted Function

// Function Declaration
hoistedFunction(); // Works fine
function hoistedFunction() {
  console.log('This function is hoisted');
}

// Function Expression
nonHoistedFunction(); // Throws an error
var nonHoistedFunction = function () {
  console.log('This function is not hoisted');
};
Enter fullscreen mode Exit fullscreen mode

In this example, hoistedFunction can be called before its definition because it is hoisted, whereas nonHoistedFunction results in an error.

Why It Matters in Interviews

Understanding hoisting demonstrates your grasp of JavaScript's compilation phase and function behaviors, which is essential for writing bug-free code.

Bonus Insights

  • Use const or let for function expressions to ensure they are defined before being called.
  • Avoid relying on hoisting for cleaner, more predictable code.

Discover the concept of hoisting with regards to functions on GreatFrontEnd

36. What Is Inheritance in ES2015 Classes?

Inheritance in ES2015 classes allows one class to extend another using the extends keyword. The super keyword is used to access the parent class's constructor and methods, enabling streamlined inheritance and method overriding.

Example: Class Inheritance

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // Calls the parent class constructor
    this.breed = breed;
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

const dog = new Dog('Rex', 'German Shepherd');
dog.speak(); // Output: Rex barks.
Enter fullscreen mode Exit fullscreen mode

In this example, Dog inherits from Animal, allowing it to reuse and override its methods.

Why It Matters in Interviews

Inheritance demonstrates your understanding of object-oriented programming in JavaScript and your ability to structure reusable and scalable code.

Bonus Insights

  • Use super in the child class to call methods or constructors from the parent class.
  • Inheritance simplifies code by promoting reusability and reducing duplication.

Learn more about the concept of inheritance in ES2015 classes on GreatFrontEnd

37. What Is Lexical Scoping?

Lexical scoping determines how variable access is resolved in nested functions based on their position in the source code. Functions can access variables defined in their outer scope.

Example: Lexical Scoping in Action

function outerFunction() {
  let outerVariable = 'I am outside!';

  function innerFunction() {
    console.log(outerVariable); // 'I am outside!'
  }

  innerFunction();
}

outerFunction();
Enter fullscreen mode Exit fullscreen mode

Here, innerFunction can access outerVariable due to lexical scoping.

Why It Matters in Interviews

Lexical scoping is a fundamental JavaScript concept. Mastering it helps in writing functions that correctly access variables in closures and nested scopes.

Bonus Insights

  • Lexical scoping lays the foundation for closures in JavaScript.
  • Use lexical scoping to simplify code by minimizing redundant variable declarations.

Discover the concept of lexical scoping on GreatFrontEnd

38. What Is Scope in JavaScript?

Scope in JavaScript defines where variables and functions are accessible in your code. There are three main types:

  1. Global Scope: Variables declared outside any function or block are accessible everywhere.
  2. Function Scope: Variables declared inside a function are only accessible within that function.
  3. Block Scope: Variables declared with let or const inside curly braces {} are only accessible within that block.

Example: Understanding Scope

// Global Scope
var globalVar = 'I am global';

function myFunction() {
  // Function Scope
  var functionVar = 'I am in a function';

  if (true) {
    // Block Scope
    let blockVar = 'I am in a block';
    console.log(blockVar); // Accessible here
  }

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

console.log(globalVar); // Accessible here
// console.log(functionVar); // Error: functionVar is not defined
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding scope is fundamental to writing robust, error-free code in JavaScript. Scope influences variable accessibility and memory management.

Bonus Insights

  • Avoid polluting the global scope by using let and const for local variables.
  • Mastering block scope with let and const is essential for modern JavaScript coding.

Learn more about the concept of scope in JavaScript on GreatFrontEnd

39. How Is the Spread Operator Used in JavaScript?

The spread operator (...) expands elements of an iterable (like arrays or objects) into individual elements. It simplifies copying, merging, and passing data.

Examples of Using the Spread Operator

  1. Copying Arrays:

    const arr1 = [1, 2, 3];
    const arr2 = [...arr1];
    
  2. Merging Arrays:

    const arr3 = [4, 5, 6];
    const mergedArray = [...arr1, ...arr3];
    
  3. Copying Objects

    const obj1 = { a: 1, b: 2 };
    const obj2 = { ...obj1 };
    
  4. Merging Objects:

    const obj3 = { c: 3, d: 4 };
    const mergedObject = { ...obj1, ...obj3 };
    
  5. Function Arguments:

    const sum = (x, y, z) => x + y + z;
    const numbers = [1, 2, 3];
    console.log(sum(...numbers)); // Output: 6
    

Why It Matters in Interviews

The spread operator is a key feature of ES6, making your code more concise and readable. It’s frequently used in frameworks like React for state updates and props handling.

Bonus Insights

  • Spread syntax only creates shallow copies. Be cautious when working with nested objects or arrays.
  • Combine with destructuring for advanced data manipulation.

Discover the concept of the spread operator and its uses on GreatFrontEnd

40. What Is this Binding in Event Handlers?

In JavaScript, the this keyword refers to the object that is executing the current code. For event handlers, this usually points to the element that triggered the event, but its behavior can vary based on how the handler is defined and invoked.

Managing this in Event Handlers

  1. Default Behavior:

    document.getElementById('btn').addEventListener('click', function () {
          console.log(this); // The button element
        });
    
  2. Arrow Functions: Arrow functions do not have their own this; they inherit it from their enclosing scope.

    document.getElementById('btn').addEventListener('click', () => {
          console.log(this); // The enclosing scope
        });
    
  3. Using bind(): Explicitly bind a specific value to this.

    const obj = { id: 1 };
    document.getElementById('btn').addEventListener(
      'click',
      function () {
        console.log(this.id); // Outputs: 1
      }.bind(obj)
    );
    

Why It Matters in Interviews

Understanding this in event handlers is critical for managing context in JavaScript, especially in complex applications like those built with React or Angular.

Bonus Insights

  • Use bind() or arrow functions to avoid common this issues.
  • Mastering this in event handlers helps ensure predictable behavior in your applications.

Learn more about the concept of this binding in event handlers on GreatFrontEnd

41. How Do Classical and Prototypal Inheritance Differ?

In object-oriented programming, classical and prototypal inheritance represent two distinct approaches to extending object behavior.

  • Classical Inheritance: Found in languages like Java or C++, this involves a strict hierarchy where classes inherit properties and methods from other classes. Objects are instantiated from predefined blueprints (classes) using constructors.

  • Prototypal Inheritance: In JavaScript, objects inherit directly from other objects via prototypes. There’s no strict class system; instead, behavior is shared or extended dynamically by linking objects.

Example: Prototypal Inheritance

const parent = {
  greet() {
    console.log('Hello from parent!');
  },
};

const child = Object.create(parent);
child.greet(); // Output: Hello from parent!
Enter fullscreen mode Exit fullscreen mode

In JavaScript, prototypal inheritance is flexible, enabling behavior delegation between objects without rigid class structures.

Why It Matters in Interviews

Understanding these paradigms highlights your grasp of JavaScript's dynamic inheritance system compared to class-based languages, showing adaptability across programming paradigms.

Bonus Insights

  • ES6 classes use prototypal inheritance under the hood, providing a familiar syntax for developers from class-based languages.
  • Prototypal inheritance is well-suited for dynamic and compositional designs in JavaScript.

Discover the difference between classical inheritance and prototypal inheritance on GreatFrontEnd

42. What’s the Difference Between querySelector() and getElementById()?

JavaScript offers different methods to select DOM elements. Here’s a comparison:

  • document.querySelector(): Uses CSS selectors to find and return the first matching element.

    const element = document.querySelector('.my-class'); // Selects the first element with class 'my-class'
    
  • document.getElementById(): Selects an element by its unique id.

    const elementById = document.getElementById('my-id'); // Selects the element with id 'my-id'
    

Key Differences

  • Flexibility: querySelector is versatile with CSS selectors (classes, attributes, pseudo-classes), while getElementById works only with id attributes.
  • Performance: getElementById is faster for single id lookups since it directly accesses elements.

Why It Matters in Interviews

Understanding these methods highlights your ability to manipulate and traverse the DOM efficiently based on different use cases.

Bonus Insights

  • Use getElementById for single, unique elements (e.g., forms or main containers).
  • Combine querySelector for complex, CSS-based selections with dynamic filtering.

Learn more about the difference between document.querySelector() and document.getElementById() on GreatFrontEnd

43. How Do Dot Notation and Bracket Notation Differ for Accessing Object Properties?

JavaScript provides two ways to access object properties: dot notation and bracket notation.

Dot Notation

  • Uses a simple, readable syntax.
  • Accesses properties with valid identifiers (e.g., no spaces, special characters).
const obj = { name: 'Alice', age: 30 };
console.log(obj.name); // Output: Alice
Enter fullscreen mode Exit fullscreen mode

Bracket Notation

  • Handles property names dynamically or with special characters.
  • Access properties with strings or variables.
const obj = { name: 'Alice', 'favorite color': 'blue' };
console.log(obj['favorite color']); // Output: blue

const prop = 'name';
console.log(obj[prop]); // Output: Alice
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding the nuances of these notations showcases your knowledge of JavaScript objects and dynamic programming practices.

Bonus Insights

  • Use dot notation for readability and bracket notation for flexibility with dynamic or unconventional property names.
  • Be mindful of potential typos in property names, especially in bracket notation.

Learn more about the difference between dot notation and bracket notation for accessing object properties on GreatFrontEnd

44. What Are the Differences Between Global, Function, and Block Scope?

In JavaScript, scope determines where variables can be accessed or modified. Here's a breakdown:

Global Scope

Variables declared in the global scope are accessible throughout the program.

var globalVar = "I'm global"; // Global scope
console.log(globalVar); // Accessible everywhere
Enter fullscreen mode Exit fullscreen mode

Function Scope

Variables declared with var inside a function are restricted to that function.

function myFunction() {
  var functionVar = "I'm in a function"; // Function scope
  console.log(functionVar); // Accessible here
}
// console.log(functionVar); // Error: functionVar is not defined
Enter fullscreen mode Exit fullscreen mode

Block Scope

Variables declared with let or const are restricted to the block they are defined in.

function myFunction() {
  if (true) {
    let blockVar = "I'm in a block"; // Block scope
    console.log(blockVar); // Accessible here
  }
  // console.log(blockVar); // ReferenceError: blockVar is not defined
}
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Understanding scope is crucial for managing variable accessibility and avoiding common issues like accidental overwrites or memory leaks.

Bonus Insights

  • Use let and const to avoid unintentional global variables.
  • Block scope improves code maintainability by limiting variable accessibility.

Discover the difference between global scope, function scope, and block scope on GreatFrontEnd

45. What’s the Difference Between Shallow Copy and Deep Copy?

A shallow copy and deep copy differ in how they duplicate objects, especially when nested objects are involved.

Shallow Copy

  • Copies only the top-level properties.
  • Nested objects remain referenced, so changes to them affect the original object.
let obj1 = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, obj1);
shallowCopy.b.c = 3;
console.log(obj1.b.c); // Output: 3
Enter fullscreen mode Exit fullscreen mode

Deep Copy

  • Creates a complete, independent duplicate, including all nested objects.
  • Changes to the copy do not affect the original object.
let deepCopy = JSON.parse(JSON.stringify(obj1));
deepCopy.b.c = 4;
console.log(obj1.b.c); // Output: 2
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Differentiating between shallow and deep copies demonstrates your understanding of JavaScript's object reference system and memory management.

Bonus Insights

  • Use libraries like lodash for robust deep copying.
  • Shallow copies are sufficient for objects without nested structures.

Discover the difference between shallow copy and deep copy on GreatFrontEnd

46. How Does Hoisting Differ for var, let, and const?

Hoisting in JavaScript determines how variable declarations are moved to the top of their scope during compilation. However, var, let, and const behave differently when hoisted.

var

  • Hoisted to the top of its scope and initialized with undefined.
  • Can be used before its declaration without causing an error.
console.log(myVar); // Output: undefined
var myVar = 'Hello';
Enter fullscreen mode Exit fullscreen mode

let and const

  • Also hoisted but not initialized (remain in the temporal dead zone until their declaration).
  • Accessing them before declaration causes a ReferenceError.
console.log(myLet); // ReferenceError: Cannot access 'myLet' before initialization
let myLet = 'World';

console.log(myConst); // ReferenceError: Cannot access 'myConst' before initialization
const myConst = '!';
Enter fullscreen mode Exit fullscreen mode

const

  • Requires an initial value at the time of declaration.
  • Once assigned, its value cannot be changed (immutable).
const PI = 3.14;
PI = 3.14159; // TypeError: Assignment to constant variable.
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

This topic tests your knowledge of JavaScript’s compilation phase and variable declarations, helping you write predictable and error-free code.

Bonus Insights

  • Prefer let and const over var for better scoping and to avoid unintended behavior.
  • Always declare variables at the top of their scope to minimize confusion.

Discover the difference in hoisting between var, let, and const on GreatFrontEnd

47. How Can Closures Be Used to Create Private Variables?

Closures in JavaScript allow you to create private variables by enclosing them within a function's scope, making them inaccessible from the outside. This is useful for encapsulation and data protection.

Example: Using Closures for Private Variables

function createCounter() {
  let count = 0; // Private variable

  return {
    increment: () => ++count,
    decrement: () => --count,
    getCount: () => count,
  };
}

const counter = createCounter();
console.log(counter.increment()); // Output: 1
console.log(counter.getCount()); // Output: 1
console.log(counter.count); // Output: undefined (not directly accessible)
Enter fullscreen mode Exit fullscreen mode

In this example, count remains private, and the only way to interact with it is through the returned methods.

Why It Matters in Interviews

Closures are a fundamental concept in JavaScript, and understanding their use for private variables demonstrates knowledge of scope, encapsulation, and functional programming.

Bonus Insights

  • Closures are also commonly used in module patterns and callbacks.
  • Combining closures with modern ES6+ syntax like arrow functions makes code more concise and readable.

Learn more about how closures can be used to create private variables on GreatFrontEnd

48. How Do Sets and Maps Handle Equality Checks for Objects?

In JavaScript, Sets and Maps use reference equality to determine if objects are the same. This means two objects are only considered equal if they reference the same memory location.

Example: Reference Equality in Sets

const set = new Set();
const obj1 = { a: 1 };
const obj2 = { a: 1 };

set.add(obj1);
set.add(obj2);

console.log(set.size); // Output: 2
Enter fullscreen mode Exit fullscreen mode

Here, obj1 and obj2 are distinct objects despite having identical properties, so they are treated as separate entries.

Why It Matters in Interviews

Understanding how Set and Map handle equality checks highlights your grasp of JavaScript's memory management and object references.

Bonus Insights

  • To handle value-based equality, consider serializing objects (e.g., using JSON.stringify) or using libraries like lodash for deep comparisons.
  • Use WeakSet or WeakMap when you need object-based keys with automatic garbage collection.

Discover how Sets and Maps handle equality checks for objects on GreatFrontEnd

49. How Can You Access the Index of an Element in an Array During Iteration?

Accessing an element's index while iterating over an array is a common task. JavaScript provides several ways to achieve this, such as forEach, map, or for...of with entries.

Example: Using forEach

const array = ['a', 'b', 'c'];
array.forEach((element, index) => {
  console.log(index, element);
});
// Output:
// 0 a
// 1 b
// 2 c
Enter fullscreen mode Exit fullscreen mode

Why It Matters in Interviews

Knowing multiple ways to iterate through arrays and access indices shows your proficiency with JavaScript’s iteration methods and ability to adapt to different coding scenarios.

Bonus Insights

  • Use Array.prototype.entries() with for...of for index-value pairs:

    for (const [index, value] of array.entries()) {
      console.log(index, value);
    }
    
  • Combining index access with functional programming methods like map allows for concise, expressive code.

Learn more about how to access the index of an element in an array during iteration on GreatFrontEnd

50. How Can You Check the Data Type of a Variable in JavaScript?

Determining a variable's data type is crucial in JavaScript for debugging and ensuring proper functionality. The primary method is using typeof, but additional techniques handle specific cases like arrays or null values.

Using typeof

The typeof operator returns a string indicating the type of the variable.

console.log(typeof 'Hello'); // Output: "string"
console.log(typeof 42); // Output: "number"
console.log(typeof true); // Output: "boolean"
console.log(typeof {}); // Output: "object"
console.log(typeof function () {}); // Output: "function"
console.log(typeof undefined); // Output: "undefined"
Enter fullscreen mode Exit fullscreen mode

Special Cases

  1. Checking Arrays:

    Use Array.isArray() to identify arrays since typeof treats arrays as "object."

    console.log(Array.isArray([1, 2, 3])); // Output: true
    console.log(typeof [1, 2, 3]); // Output: "object"
    
  2. Checking null:

    Use variable === null because typeof null returns "object" (a known quirk in JavaScript).

    const value = null;
    console.log(value === null); // Output: true
    console.log(typeof value); // Output: "object"
    

Why It Matters in Interviews

Understanding how to check data types demonstrates your knowledge of JavaScript quirks and ensures proper variable handling in various contexts.

Bonus Insights

  • Use TypeScript: If you frequently handle complex data types, TypeScript provides static type checking to reduce runtime errors.
  • Deep Type Checking: Libraries like Lodash or custom helper functions can check for more complex data structures.

Discover how to check the data type of a variable on GreatFrontEnd

Conclusion

🎉 Congratulations on reaching the end of our JavaScript interview Q&A guide! 🚀 We hope it’s been your trusty companion in boosting your skills and confidence for that upcoming interview. 💻✨ Practice makes perfect—so keep coding, reviewing, and owning those concepts until they’re second nature. 💪

👉 Need more guidance or look for a straightforward yet effective roadmap to successful frontend interviews ? Check out GreatFrontEnd for expert-crafted resources! 🌟 Let’s make your coding journey unforgettable! 💡

Top comments (0)