JavaScript Coding Interview Questions

author image Hirely
at 05 Jan, 2025

Question: What is prototypal inheritance in JavaScript?

Answer:

Prototypal inheritance is a mechanism in JavaScript that allows an object to inherit properties and methods from another object. This is different from classical inheritance (used in languages like Java or C++), where classes and instances are used. In JavaScript, inheritance is based on objects and their prototype chains , rather than classes.In JavaScript, every object has an internal property called [[Prototype]]** , which points to another object. This is what forms the prototype chain. If a property or method is not found in the object itself, JavaScript will look for it in the object’s prototype, and then in the prototype’s prototype, and so on, up the prototype chain, until it either finds the property or reaches the end of the chain (which is null).

Key Concepts:

  1. Prototype : Every JavaScript object has a prototype, which is also an object. This prototype itself can have its own prototype, forming a chain. The prototype object can contain properties and methods that are shared across instances.

  2. Prototype Chain : The prototype chain is the chain of objects connected through the [[Prototype]] property. When a property or method is accessed on an object, JavaScript will first look at the object itself. If it doesn’t find it, it will check the object’s prototype, and continue up the chain until it finds the property or method or reaches the end (null).

  3. __proto__ and prototype :

  • __proto__ is a reference to the object’s prototype and can be used to access the prototype of an object.

  • prototype is a property of constructor functions (which are essentially special functions used to create objects). Every function in JavaScript has a prototype property that points to an object from which instances of that function inherit properties and methods.

Example of Prototypal Inheritance:

// Constructor function for creating a Person object
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Adding a method to the Person prototype
Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// Creating a new Person object
const person1 = new Person('Alice', 30);
person1.greet(); // Hello, my name is Alice and I am 30 years old.

Explanation :

  • Person.prototype.greet adds a method greet to the prototype of Person. Every instance of Person, such as person1, can access this method because it inherits from Person.prototype.

  • The greet method is not defined directly on person1, but is found through the prototype chain.

How Prototypal Inheritance Works:

  1. Object Creation : When you create an object, it gets an internal prototype ([[Prototype]]) that links to the prototype of the constructor function used to create it.

  2. Property Lookup : When you try to access a property or method on an object, JavaScript will first look at the object itself. If it doesn’t find the property, it will look at the object’s prototype ([[Prototype]]). This continues up the prototype chain until the property is found or null is reached.

Example of Inheritance Using Prototypes:

// Constructor function for a Person
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Method added to the Person prototype
Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}`);
};

// Constructor function for an Employee, inheriting from Person
function Employee(name, age, jobTitle) {
  Person.call(this, name, age); // Inheriting properties from Person
  this.jobTitle = jobTitle;
}

// Set the prototype of Employee to an instance of Person
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;

// Adding a method to the Employee prototype
Employee.prototype.describeJob = function() {
  console.log(`I am a(n) ${this.jobTitle}`);
};

// Create an Employee instance
const employee1 = new Employee('Bob', 35, 'Software Engineer');
employee1.greet(); // Inherited from Person prototype: Hello, my name is Bob
employee1.describeJob(); // I am a(n) Software Engineer

Explanation :

  • Person.call(this, name, age) is used to inherit the properties (name and age) from the Person constructor.

  • Employee.prototype = Object.create(Person.prototype) sets up the prototype chain so that Employee inherits from Person. This means that employee1 can access methods like greet from Person.prototype.

  • Employee.prototype.constructor = Employee ensures that the constructor property of Employee points to Employee and not Person.

The Prototype Chain:

When employee1.greet() is called:

  1. JavaScript first looks for the greet method on employee1.

  2. Since it is not found on employee1, it looks at Employee.prototype (because employee1 inherits from Employee.prototype).

  3. Since greet is not found on Employee.prototype, it looks at Person.prototype (because Employee.prototype is an instance of Person.prototype).

  4. greet is found on Person.prototype, so the method is called. Object.create() and Prototypal Inheritance:Object.create() is a method that allows you to create a new object with a specific prototype.

const animal = {
  speak: function() {
    console.log('Animal speaking');
  }
};

const dog = Object.create(animal); // dog inherits from animal
dog.speak(); // Animal speaking

In this example, dog inherits from animal, so when dog.speak() is called, it uses the speak method from animal.

Key Points to Remember:

  • Prototypal inheritance allows objects to inherit properties and methods from other objects.

  • Every JavaScript object has a prototype, and the prototype chain is used to look for properties and methods.

  • Constructor functions are used to create objects, and each constructor function has a prototype property that defines what objects created with it will inherit.

  • Inheritance can be set up manually by manipulating prototypes, typically using Object.create() and setting prototype chains explicitly.

Summary:

Prototypal inheritance in JavaScript is a way for objects to inherit behavior from other objects via the prototype chain. Instead of using classes, JavaScript allows objects to directly inherit from other objects, which can be dynamically extended or modified. Understanding this model is crucial for working with JavaScript’s inheritance system and object-oriented design.

Question: What are Promises in JavaScript?

Answer:

A Promise in JavaScript is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It is a way to handle asynchronous operations more effectively, avoiding the so-called “callback hell” (or “pyramid of doom” ), which occurs when multiple callbacks are nested inside each other.A Promise is used to handle asynchronous computations and is a more readable and manageable way of dealing with operations like network requests, file reading, timers, or any task that takes time to complete.

Key Concepts of Promises:

  1. State of a Promise : A Promise can be in one of the following three states:
  • Pending : The Promise is still in progress and has not been completed or rejected.

  • Fulfilled (Resolved) : The asynchronous operation was successful, and the Promise has resolved with a value.

  • Rejected : The asynchronous operation failed, and the Promise was rejected with a reason (usually an error).

  1. Thenable : A thenable is an object or function that has a .then() method. A Promise is a type of thenable, and then() is used to define what should happen when the Promise is fulfilled or rejected.

  2. Chaining : Promises allow you to chain .then() and .catch() methods to handle subsequent actions after the promise has been resolved or rejected.

How Promises Work:

  • A Promise is created using the new Promise() constructor. The constructor takes an executor function that has two parameters: resolve and reject. resolve() is called when the Promise is successfully fulfilled, and reject() is called when there is an error.

  • The The then() method is used to specify what happens when the Promise is fulfilled, and The The then() method is used to specify what happens when the Promise is fulfilled, and catch() is used to specify what happens when the Promise is rejected.

Syntax:

let promise = new Promise(function(resolve, reject) {
  // Asynchronous operation
  let success = true;
  
  if (success) {
    resolve('Operation successful');  // Fulfilled state
  } else {
    reject('Operation failed');  // Rejected state
  }
});

promise
  .then(function(value) {
    console.log(value);  // This runs if the promise is fulfilled
  })
  .catch(function(error) {
    console.log(error);  // This runs if the promise is rejected
  });

Example of Using a Promise:

Here’s an example of a Promise that simulates an asynchronous operation (e.g., fetching data from an API) and either resolves or rejects based on some condition:

let fetchData = new Promise(function(resolve, reject) {
  let dataFetched = true;

  if (dataFetched) {
    resolve('Data fetched successfully!');
  } else {
    reject('Failed to fetch data.');
  }
});

fetchData
  .then(function(result) {
    console.log(result);  // Data fetched successfully!
  })
  .catch(function(error) {
    console.error(error);  // This won't run in this case, since the promise is resolved
  });

If the dataFetched variable is set to false, the catch() block would run instead, logging the rejection message.

Chaining Promises:

Promises allow you to chain multiple .then() methods to handle a sequence of asynchronous operations:

let promise1 = new Promise(function(resolve, reject) {
  resolve('First step completed');
});

promise1
  .then(function(result) {
    console.log(result); // First step completed
    return 'Second step completed';  // Returning a value to the next .then()
  })
  .then(function(result) {
    console.log(result); // Second step completed
    return 'Third step completed';
  })
  .then(function(result) {
    console.log(result); // Third step completed
  });

Each .then() returns a new promise, and the next .then() will be executed only after the previous one has completed.

Handling Errors:

The .catch() method is used to handle errors in any of the steps of the promise chain:

let promise = new Promise(function(resolve, reject) {
  reject('Something went wrong!');
});

promise
  .then(function(value) {
    console.log(value);
  })
  .catch(function(error) {
    console.log('Error: ' + error); // Error: Something went wrong!
  });

If any of the Promises in the chain rejects, the catch() method will be called.The finally() Method:The .finally() method is used to execute code after the promise has settled, regardless of whether it was fulfilled or rejected. This is useful for performing cleanup actions like hiding loading indicators.

let promise = new Promise(function(resolve, reject) {
  let success = true;

  if (success) {
    resolve('Operation successful');
  } else {
    reject('Operation failed');
  }
});

promise
  .then(function(result) {
    console.log(result);  // Operation successful
  })
  .catch(function(error) {
    console.log(error);  // This won't run in this case
  })
  .finally(function() {
    console.log('Cleanup done!');  // Always runs after the promise settles
  });

Example with setTimeout:Here’s an example using setTimeout to simulate an asynchronous operation that resolves after 2 seconds:

let timerPromise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve('Timer completed');
  }, 2000);
});

timerPromise
  .then(function(result) {
    console.log(result);  // Timer completed
  })
  .catch(function(error) {
    console.log(error);
  });

Promise.all() and Promise.race():

  • Promise.all() : Takes an array of promises and returns a new promise that resolves when all of the promises in the array have resolved. If any of the promises reject, the whole Promise.all() call will reject.
let p1 = Promise.resolve('First');
let p2 = Promise.resolve('Second');

Promise.all([p1, p2])
  .then(function(results) {
    console.log(results);  // ['First', 'Second']
  })
  .catch(function(error) {
    console.log(error);
  });
  • Promise.race() : Returns a promise that resolves or rejects as soon as one of the promises in the array resolves or rejects. The first promise to settle (either resolve or reject) wins.
let p1 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'First');
});
let p2 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 200, 'Second');
});

Promise.race([p1, p2])
  .then(function(result) {
    console.log(result);  // First (because it resolves first)
  });

Key Points:

  • Promise represents the eventual completion or failure of an asynchronous operation.

  • A Promise can be in one of three states: pending, fulfilled, or rejected.

  • Promises allow chaining with Promises allow chaining with .then() , Promises allow chaining with Promises allow chaining with .then() , .catch() , and Promises allow chaining with Promises allow chaining with .then() , Promises allow chaining with Promises allow chaining with .then() , .catch() , and .finally() to handle results and errors.

  • Promise.all() and Promise.all() and Promise.race() are used to work with multiple promises at once.

Summary:

A Promise in JavaScript is an object that helps manage asynchronous operations. By using Promises, JavaScript makes it easier to handle long-running operations like network requests or timers in a cleaner, more readable way, avoiding nested callbacks. Promises are essential for modern JavaScript development and are commonly used for working with async code, ensuring better flow and error handling in applications.

Read More

If you can’t get enough from this article, Aihirely has plenty more related information, such as JavaScript interview questions, JavaScript interview experiences, and details about various JavaScript job positions. Click here to check it out.

Related Posts

Trace Job opportunities

Hirely, your exclusive interview companion, empowers your competence and facilitates your interviews.

Get Started Now