![]() |
VOOZH | about |
JavaScript is a prototype-based language, meaning object properties and methods can be shared through generalized objects that have the ability to be cloned and extended. This is known as prototypical inheritance and differs from class inheritance. Among popular object-oriented programming languages, JavaScript is relatively unique, as other prominent languages such as PHP, Python, and Java are class-based languages, which instead define classes as blueprints for objects.
In this tutorial, we will learn what object prototypes are and how to use the constructor function to extend prototypes into new objects. We will also learn about inheritance and the prototype chain.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
JavaScript is a high-level, object-based, dynamic scripting language popular as a tool for making webpages interactive.
Browse Series: 37 tutorials
Software engineer and open source creator
Community and Developer Education expert. Former Senior Manager, Community at DigitalOcean. Focused on topics including Ubuntu 22.04, Ubuntu 20.04, Python, Django, and more.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Could you please put this part a bit more clear ? - “This prototype chain is only one link long. x -> Object. We know this, because if we try to chain two [[Prototype]] properties together, it will be null.”
This article is not as good as the others. It’s a bit confusing, because the author doesn’t explain the concept or prototyping and the reasons behind it.
I am just learning about prototypes, but one part of this didn’t seem right to me, and after trying out some things I think I can explain what I mean. It’s this part:
// Link prototypes and add prototype methods
Warrior.prototype = Object.create(Hero.prototype);
Healer.prototype = Object.create(Hero.prototype);
This isn’t really linking the objects, it’s more like replacing them. The point of the prototype chain as far as I can tell is that you can link them to form a kind of inheritance, without losing attributes, but this will lose them. Here’s what I mean:
function Hero(name, level){
this.name = name;
this.level=level;
}
function Healer(name, level, spell){
Hero.call(this, name, level);
this.spell = spell;
}
Hero.prototype.greet = function(){return "I am " + this.name}
Healer.prototype.heal = function(){return this.name + " casts " + this.spell}
var healer = new Healer("Pyro", 126, "Resta");
healer.heal()
// "Pyro casts Resta"
healer.greet() // undefined, as expected, not linked yet
// VM1872:1 Uncaught TypeError: healer.greet is not a function
// Now "link" the prototypes
Healer.prototype = Object.create(Hero.prototype)
healer.greet() // healer linked to old Healer prototype still, have to construct it again to pick up the change
// VM1872:1 Uncaught TypeError: healer.greet is not a function
healer = new Healer("Pyro", 126, "Resta");
healer.greet()
// "I am Pyro"
healer.heal() // oops
// VM2045:1 Uncaught TypeError: healer.heal is not a function
healer.constructor // Thinks its constructor is hero's
// ƒ Hero(name, level){
// this.name = name;
// this.level=level;
// }
So, as you can see, swapping out prototypes can lead to some really strange behavior. I found this out the hard way trying examples in this article.
Now, if you changed my example to define methods on Hero’s prototype first, then copy Hero’s to Healer’s, then define new methods on Healer’s, that works, but that is unnecessarily finicky seems to defeat the purpose of inheritance.
Another problem with the way in the article is that “Healer.prototype” begins with a reference to the “Healer” constructor. When you override the “prototype” property, “Healer.prototype.constructor” now point’s to “Hero’s” constructor. This makes things pretty ambiguous, because now healer, created with Healer’s constructor, thinks its constructor is actually Hero’s.
Rather, I think you should set the [[Prototype]] property of Healer’s prototype to point to Hero’s prototype. See this example for what seem like a better way to me:
function Hero(name, level){
this.name = name;
this.level=level;
}
function Healer(name, level, spell){
Hero.call(this, name, level);
this.spell = spell;
}
Hero.prototype.greet = function(){return "I am " + this.name}
Healer.prototype.heal = function(){return this.name + " casts " + this.spell}
var healer = new Healer("Pyro", 126, "Resta");
healer.heal()
// "Pyro casts Resta"
healer.greet() // undefined, as expected
// VM1872:1 Uncaught TypeError: healer.greet is not a function
// Instead of setting Healer.prototype, we're setting [[Prototype]] of Healer.prototype
Object.setPrototypeOf(Healer.prototype, Hero.prototype);
healer.greet() // This is now found, by traversing the prototype chain. You don't have to reassign healer.
// "I am Pyro"
healer.heal() // healer's prototype is still Healer.prototype so this is fine
// "Pyro casts Resta"
// because prototypes are linked properly, children of Hero automatically obtain new methods defined on Hero
Hero.prototype.charge=function(){return "CHARGE!!"}
healer.charge() // didn't need to change anything on healer object or Healer function
// "CHARGE!!"
healer.constructor // healer still has proper constructor
// ƒ Healer(name, level, spell){
// Hero.call(this, name, level);
// this.spell = spell;
// }
This seems to work a lot better: the order in when you declare things doesn’t matter, Healer’s prototype information is preserved. When you look at the structure of the object in the console, you see what looks more like proper inheritance. Let me know what you think.
Hey! There is a slight bug in your example code.
Warrior.prototype = Object.create(Hero.prototype) would actually replace all of the contents of the original Warrior prototype. Methods can be restored with proper sequence of statements in code. But what cannot be restored is a constructor property, which in turn is used by things like instanceof.
This could result into something like:
const warrior = new Warrior();
console.log(warrior instanceof Hero); // true
console.log(warrior instanceof Warrior); // false
That is, the prototype chain is broken, which is most probably not what we intended.
What one should do instead is:
Warrior.prototype.__proto__ = Object.create(Hero.prototype);
That would properly link Warrior to Hero’s whatever prototype chain correctly.
Also, you can safely get rid of Object.create() because this intermediate empty object doesn’t add any value.
So finally constructor function prototype’s linkage would look something like this:
Warrior.prototype.__proto__ = Hero.prototype;
In fact, if you look inside objects created with new ES2015 class syntax from class hierarchies using extend, you will find out that prototypes are linked exactly that way.
Also, it would be quite nice if you would explain the exact difference between prototype and __proto__ properties. It can be a source of greate confusion for the beginners. Especially when encountered in expressions like Warrior.prototype.__proto__ = Hero.prototype.
Uma delicia de conteudo. mulher é demais
Nice content! Very helpful :)
Just a heads-up: I guess “Object.setPropertyOf()” should be “Object.setPrototypeOf()” here
We will use to link the properties in the
Heroconstructor to theWarriorandHealerconstructors, making sure to put it before any additional methods.
wanted to mention this little quirk with chrome dev tools and
thanks a lot i had struggled so much to understand this topic but this was hella helpful thank you
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.