Prototypes are the mechanism by which JavaScript objects inherit methods and properties from one another. Prototypes are important part of JavaScript and therefore it's important to understand the basics of this concept.
The goal of this article is to show how prototype object of constructor functions is used to pass methods and properties to its instances.
In JavaScript, functions are objects. They are instances of the Function() constructor and this way every created function inherit certain methods and properties. One such inherited property is prototype. Initial value of the prototype is an empty object. Arrow functions are an exception, they do not have the prototype object and cannot be used as a constructor.
Let's create a simple function and we'll see that by default it contains the prototype property
function myFunction(){
return a + b
}
console.log(myFunction)
console.log(myFunction.prototype) // {}
Now, we can add methods and properties to this prototype object and they will not effect the function itself. They will only be used if the function is used as a constructor and this way its instances would inherit methods and properties from the prototype object.
Let's create a constructor function named Device() so that we can observe how this prototype object actually works.
function Device(name, color) {
this.name = name;
this.color = color;
}
Now, we create someDevice instance using new operator:
const someDevice = new Device("monitor", "silver")
console.log(someDevice)
As we can see, our someDevice object has two properties, that is name and color. Let's say, we want every object created with Device() constructor to have a priceRequired property with boolean value of true. For that, we can use the prototype object of the constructor.
Device.prototype.priceRequired = true
console.log(Device.prototype)
Now we can see that prototype object of the Device() constructor is no longer empty as it contains the priceRequired property. Every object that was created and that will be created using the Device() constructor will detect these changes. Our someDevice object now has access to this new property.
console.log(someDevice.priceRequired) // true
We can recognize that someDevice object has two own or direct properties. Those properties are name and color. We can check that using the hasOwnProperty() method.
console.log(someDevice.hasOwnProperty("name")) // true
console.log(someDevice.hasOwnProperty("color")) // true
What will happen if we check the priceRequired property like we've done with name and color? We'll get false value because the priceRequired in this case is inherited property.
console.log(someDevice.hasOwnProperty("priceRequired")) // false
We created priceRequired property inside the prototype object of the Device() constructor function. Therefore the priceRequired is direct property of the prototype object.
console.log(Device.prototype.hasOwnProperty("priceRequired")) // true
If we need access to a certain property, JavaScript will always first look up own properties of an object. Then, if JavaScript does not find it there, it will recognize the prototype object of its constructor and will search there for the property. If the property is not found it will return undefined.
The reason the priceRequired is not a direct property of someDevice is that in JavaScript objects are copied by reference and not by value. That means, the prototype object of a constructor function is not cloned into every instance but the instances have a 'link' to the prototype object. In practice this means that any modification in the prototype object will be recognized by the instances.
console.log(someDevice.priceRequired === someDevice.constructor.prototype.priceRequired); // true
console.log(someDevice.priceRequired === Device.prototype.priceRequired); // true
console.log(someDevice.constructor.prototype === Device.prototype); // true
Here we can see that there is only one copy of priceRequired property and only one copy of prototype object of Device() constructor function. That's why we get true for these comparisons.
Now, if we change the value of priceRequired to false, all instances, the already created ones and those that will be created will recognize this change.
Device.prototype.priceRequired = false;
console.log(someDevice.priceRequired) // false
Every JavaScript object has an "internal" prototype property which points to the object from which it directly inherits. It's called [[prototype]] in Chrome and < prototype > in Firefox. Getter for this internal property is Object.getPrototypeOf(). We are going to inspect someDevice in the console.
console.log(someDevice)
We notice that < prototype > points directly to the prototype object of the Device() constructor function.
- Object Oriented JavaScript by Ved Antani and Stoyan Stefanov
- https://stackoverflow.com/questions/12661416/it-is-said-that-all-javascript-objects-have-a-prototype-property-but-i-only-see