JavaScript Coding Interview Questions
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:
-
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.
-
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
). -
__proto__
andprototype
:
-
__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 aprototype
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 methodgreet
to the prototype ofPerson
. Every instance ofPerson
, such asperson1
, can access this method because it inherits fromPerson.prototype
. -
The
greet
method is not defined directly onperson1
, but is found through the prototype chain.
How Prototypal Inheritance Works:
-
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. -
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 ornull
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
andage
) from thePerson
constructor. -
Employee.prototype = Object.create(Person.prototype)
sets up the prototype chain so thatEmployee
inherits fromPerson
. This means thatemployee1
can access methods likegreet
fromPerson.prototype
. -
Employee.prototype.constructor = Employee
ensures that theconstructor
property ofEmployee
points toEmployee
and notPerson
.
The Prototype Chain:
When employee1.greet()
is called:
-
JavaScript first looks for the
greet
method onemployee1
. -
Since it is not found on
employee1
, it looks atEmployee.prototype
(becauseemployee1
inherits fromEmployee.prototype
). -
Since
greet
is not found onEmployee.prototype
, it looks atPerson.prototype
(becauseEmployee.prototype
is an instance ofPerson.prototype
). -
greet
is found onPerson.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 settingprototype
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:
- 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).
-
Thenable : A thenable is an object or function that has a
.then()
method. A Promise is a type of thenable, andthen()
is used to define what should happen when the Promise is fulfilled or rejected. -
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
andreject
.resolve()
is called when the Promise is successfully fulfilled, andreject()
is called when there is an error. -
The The
then()
method is used to specify what happens when the Promise is fulfilled, and The Thethen()
method is used to specify what happens when the Promise is fulfilled, andcatch()
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 wholePromise.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
, orrejected
. -
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()
andPromise.all()
andPromise.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.
Tags
- JavaScript
- Interview questions
- JavaScript basics
- Closures
- Event loop
- Asynchronous programming
- Callback functions
- Promises
- Async/await
- Prototypal inheritance
- DOM
- Data types
- Error handling
- Arrow functions
- Function expressions
- Function declarations
- Spread operator
- Object cloning
- JavaScript arrays
- Event delegation
- Synchronous vs asynchronous
- Object oriented programming
- ES6
- JavaScript syntax
- Debugging JavaScript
- JavaScript functions
- JavaScript scoping
- JavaScript keywords