Function Prototype
Prototypes / Inheritance: The Prototype Property
What is the prototype property in JavaScript?
View Answer:
let animal = {
eats: true,
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal; // references animal
let rabbit = new Rabbit('White Rabbit'); // rabbit.__proto__ == animal
console.log(rabbit.eats); // true
Can you explain how the prototype property works in JavaScript?
View Answer:
function Rabbit() {}
// by default:
// Rabbit.prototype = { constructor: Rabbit }
let rabbit = new Rabbit(); // inherits from {constructor: Rabbit}
console.log(rabbit.constructor == Rabbit); // true (from prototype)
What happens when you replace the default prototype in JavaScript?
View Answer:
function Rabbit() {}
Rabbit.prototype = {
jumps: true,
};
let rabbit = new Rabbit();
console.log(rabbit.constructor === Rabbit); // false
function Dog() {}
let dog = new Dog();
console.log(dog.constructor === Dog); // true
What is the difference between "proto" and prototype?
View Answer:
First let's clarify the concepts.
In JavaScript, each object has a __proto__ property which is an internal reference to the prototype object from which the instance object inherited. The prototype object is special type of enumerable object to which additional properties can be attached to it which will be shared across all the instances of its constructor function.
Here's an example that showcases the difference:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.getFullName = function() {
return this.firstName + ' ' + this.lastName;
}
let john = new Person('John', 'Doe');
console.log(john.__proto__); // This will output the prototype object of the "john" instance
console.log(Person.prototype); // This will output the prototype object of the "Person" constructor function
console.log(john.__proto__ === Person.prototype); // This will output true, meaning both refer to the same object
console.log(john.getFullName()); // Output: John Doe. This is because "getFullName" method is defined in Person's prototype, so it's accessible to "john" instance.
In this example, you can see that john.__proto__ and Person.prototype both refer to the same object. This is because john was created with the Person constructor, so its prototype (__proto__) is the same as Person.prototype.
The getFullName method is defined on Person.prototype, meaning it's not directly attached to john object. However, it's still accessible to john because john's __proto__ points to Person.prototype.
Note: The __proto__ property is considered deprecated and non-standard. It's better to use Object.getPrototypeOf(object) method to get the prototype of an object.
How does prototypal inheritance work in JavaScript?
View Answer:
What is the purpose of the Object.prototype property?
View Answer:
Can you modify the prototype of an existing object?
View Answer:
let animal = {
speaks: true
};
let dog = {
bark: function() {
return 'Woof!';
}
};
// dog is an ordinary object, it doesn't have the 'speaks' property
console.log(dog.speaks); // undefined
// Set animal to be the prototype of dog
Object.setPrototypeOf(dog, animal);
// Now dog has 'speaks' property from its prototype chain
console.log(dog.speaks); // true
// dog can also access the 'bark' method that's directly on it.
console.log(dog.bark()); // 'Woof!'
In this code, dog doesn't initially have a speaks property. When animal is set as the prototype of dog, the dog object can then access animal's speaks property.
Note: While it's possible to change the [[Prototype]] of an object, it's considered a bad practice in production code because it can lead to performance problems. In general, it's better to create the right prototype chain when creating objects. This method is there for completeness and should be used sparingly, if at all.
What happens if you look for a property or a method that's not present in the object but exists in the prototype chain?
View Answer:
let animal = {
speaks: true,
sound: function() {
return 'Generic animal sound!';
}
};
let dog = Object.create(animal); // animal is the prototype of dog
dog.bark = function() {
return 'Woof!';
};
console.log(dog.bark()); // 'Woof!', since 'bark' method exists directly on the 'dog' object
console.log(dog.speaks); // true, 'speaks' property doesn't exist directly on the 'dog', but exists in the prototype chain (in 'animal')
console.log(dog.sound()); // 'Generic animal sound!', 'sound' method doesn't exist directly on the 'dog', but exists in the prototype chain (in 'animal')
console.log(dog.meow); // undefined, 'meow' neither exists directly on the 'dog' nor in the prototype chain
In this example, when we call dog.speaks or dog.sound(), JavaScript first checks if these properties/methods exist directly on the dog object. Since they don't, JavaScript then checks dog's prototype, which is animal. Since animal has the speaks property and sound method, these values are returned.
However, when we try to access dog.meow, JavaScript first checks the dog object, and then its prototype animal. Since neither has a meow property, undefined is returned.
What is a prototype chain?
View Answer:
What is the role of the constructor property?
View Answer:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
let john = new Person('John', 'Doe');
console.log(john.constructor); // [Function: Person]
In this code, john.constructor is Person, which is the function used to create john.
The constructor property is also useful when you want to create a new instance and you only have an instance of the object, but not the original constructor. Here's an example:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
let john = new Person('John', 'Doe');
let jane = new john.constructor('Jane', 'Doe');
console.log(jane.firstName); // Output: Jane
console.log(jane.lastName); // Output: Doe
Here, john.constructor refers to the Person function, which we can use to create a new Person.
Note that the constructor property can be overridden, so it's not a completely reliable way to determine the constructor of an object. The instanceof operator is generally a better choice for that.
What's the difference between prototypal and classical inheritance?
View Answer:
Let's see an example of both:
Classical Inheritance (Simulation in JavaScript)
// Constructor for Superclass
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
return this.name + ' makes a sound.';
}
// Constructor for Subclass
function Dog(name) {
Animal.call(this, name); // Call the parent's constructor
}
// Establish the prototype chain to inherit methods
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // Repair the constructor reference
Dog.prototype.bark = function() {
return this.name + ' barks.';
}
var dog = new Dog('Rex');
console.log(dog.speak()); // Output: Rex makes a sound.
console.log(dog.bark()); // Output: Rex barks.
In this example, we simulate classical inheritance using constructor functions. Dog is a subclass of Animal and inherits its methods.
Prototypal Inheritance
var animal = {
init: function(name) {
this.name = name;
},
speak: function() {
return this.name + ' makes a sound.';
}
};
var dog = Object.create(animal);
dog.bark = function() {
return this.name + ' barks.';
}
var rex = Object.create(dog);
rex.init('Rex');
console.log(rex.speak()); // Output: Rex makes a sound.
console.log(rex.bark()); // Output: Rex barks.
In this prototypal inheritance example, we directly create objects from other objects. rex is an object created from dog, which is created from animal. The methods from animal are available to dog and rex through the prototype chain.
In prototypal inheritance, an object can directly inherit from another object. This is different from classical inheritance where classes inherit from classes.
What are some common pitfalls with prototypes and inheritance in JavaScript?
View Answer:
What is the prototype property used for?
View Answer:
Do all objects in JavaScript have a prototype?
View Answer:
let obj = {};
console.log(obj.__proto__ === Object.prototype); // true
function Func() {}
console.log(Func.__proto__ === Function.prototype); // true
let arr = [];
console.log(arr.__proto__ === Array.prototype); // true
In this code, obj is an object literal, so its prototype is Object.prototype. Func is a function, so its prototype is Function.prototype. arr is an array, so its prototype is Array.prototype.
However, it's possible to create an object without a prototype using Object.create(null). Such objects do not inherit anything, including basic methods like toString():
let noProto = Object.create(null);
console.log(noProto.__proto__); // undefined
In this code, noProto does not have a prototype, so noProto.__proto__ is undefined. Attempting to call toString() on noProto would result in an error.
What is shadowing in JavaScript with regards to prototypes?
View Answer:
let animal = {
speak: function() {
return 'The animal makes a sound!';
}
};
let dog = Object.create(animal);
dog.speak = function() {
return 'The dog barks!';
};
console.log(dog.speak()); // Output: The dog barks!
In this code, dog is created with animal as its prototype. animal has a speak method, and dog also has a speak method. When dog.speak() is called, JavaScript first looks for a speak method on the dog object. Since it finds one, it uses that method and does not continue looking up the prototype chain.
If the speak method is deleted from dog, then the speak method from animal is used instead:
delete dog.speak;
console.log(dog.speak()); // Output: The animal makes a sound!
In this code, after the speak method is deleted from dog, dog.speak() outputs 'The animal makes a sound!'. This is because JavaScript doesn't find a speak method on dog, so it looks up the prototype chain and finds the speak method on animal.
Can you remove properties from a prototype?
View Answer:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.getFullName = function() {
return this.firstName + ' ' + this.lastName;
}
let john = new Person('John', 'Doe');
console.log(john.getFullName()); // Output: John Doe
// Deleting property from prototype
delete Person.prototype.getFullName;
console.log(john.getFullName); // Output: undefined
In this example, john.getFullName() initially outputs 'John Doe'. After getFullName is deleted from Person.prototype, john.getFullName is undefined.
While it's possible to delete properties from a prototype, it's generally not a good idea because it can have unexpected side effects. For example, if other code is depending on that property being present in the prototype, that code could stop working correctly.
Can we use arrow functions for prototype methods?
View Answer:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.getFullName = () => {
return this.firstName + ' ' + this.lastName;
}
let john = new Person('John', 'Doe');
console.log(john.getFullName()); // Output: undefined undefined
In this case, the getFullName method is an arrow function, so this doesn't refer to the john object. Instead, it refers to the surrounding scope, which in a non-strict global context is the window object (or global object in Node.js environment). Since window.firstName and window.lastName are not defined, john.getFullName() returns "undefined undefined".
Compare this with a traditional function:
Person.prototype.getFullName = function() {
return this.firstName + ' ' + this.lastName;
}
console.log(john.getFullName()); // Output: John Doe
In this case, the getFullName method is a traditional function, so this refers to the john object. john.getFullName() therefore correctly returns "John Doe".
For this reason, arrow functions are generally not used as object methods when those methods need to access other properties of the object.
Why do we say JavaScript has a dynamic prototype?
View Answer:
How does the JavaScript engine find a property in a prototype chain?
View Answer:
What is the difference between a prototype and an instance in JavaScript?
View Answer:
// Define a constructor function
function Car(make, model) {
this.make = make;
this.model = model;
}
// Add a method to the prototype
Car.prototype.displayCar = function() {
return this.make + ' ' + this.model;
}
// Create a new instance of Car
let myCar = new Car('Toyota', 'Corolla');
console.log(myCar.displayCar()); // Outputs: Toyota Corolla
In this code:
Caris a constructor function. It defines a blueprint for creating new car objects.Car.prototype.displayCaris a method added to the prototype ofCar. This method will be shared by all instances ofCar.myCaris an instance ofCar. It's an object created from theCarconstructor, and it has access to properties and methods defined in theCarconstructor and theCarprototype.
So the main difference is that an instance is an individual object created from a constructor, while a prototype is an object that serves as a blueprint for instances. Changes to the prototype affect all instances, while changes to an instance only affect that instance.
What is the default prototype of an object created using an object literal?
View Answer:
What is Object.create() and how does it relate to prototypes?
View Answer:
let animal = {
speaks: true,
sound: function() {
return 'Generic animal sound!';
}
};
let dog = Object.create(animal);
dog.bark = function() {
return 'Woof!';
};
console.log(dog.speaks); // true, inherited from 'animal' via prototype chain
console.log(dog.sound()); // 'Generic animal sound!', inherited from 'animal' via prototype chain
console.log(dog.bark()); // 'Woof!', present directly on 'dog'
In this example, animal is used as a prototype for creating dog with Object.create(). As a result, dog has access to the speaks property and the sound method via the prototype chain, while also having its own bark method directly on it.