I'm trying to understand how prototypal inheritance works when using constructor functions in Javascript. Take the following code for example:
let Animal = function (name, species, birthYear) {
this.name = name;
this.species = species;
this.birthYear = birthYear;
};
let spike = new Animal(`Spike`, `Dog`, 2000);
Animal.prototype.calculateAge = function () {
let age = 2022 - this.birthYear;
return age;
};
Animal.prototype.color = `brown`;
spike.calculateAge();
console.log(spike.color);
console.log(spike);
These last three lines are what I'm struggling to understand. Please let me know if my understanding is misguided at any point in my explanation.
I can call the calculateAge() method on the spike object because it inherited the method from the Animal prototype. Similarly, I can log spikes color property to the console because the spike object inherited the color property from the Animal prototype. When I inspect the spike object using console.log(), it has the properties of name, species, and birthYear that I defined, but there is also a prototype. Does this mean that the spike object contains the prototype, or is this simply identifying the prototype from which it inherited the aforementioned method and property? Also, I don't really feel like I understand what this Animal prototype is. I get that the code works and that's all well and good, but just what in the world is this Animal prototype thing? Is it its own object in and of itself?
Thank you for any explanation you can offer.
I think you should first deeply understand the object oriented paradigm behind Javascript.
Javascript is a prototype based object oriented programming language.
The operations are encoded in the prototype data structure in a prototype language, which is copied and updated at run time.
However, a class is still the equivalence class for all objects with the same state space and methods when viewed abstractly.
You're effectively creating an element of a new equivalence class when you add a method to the prototype.
So, why are you doing that? Mostly because it results in a run-time system that is straightforward, logical, and elegant. To create a new object or class, simply do a deep copy, which copies all of the data as well as the prototype data structure. Then you get inheritance and polymorphism for almost nothing: A method lookup always entails requesting a method implementation by name from a dictionary.
To be more specific, JavaScript is a prototype-based object-oriented language, which means it doesn't have classes and instead defines behaviors using constructor functions, which can then be reused using the prototype.
Related
please help me get it straight, I see every object in javascript leads to object Object in prototype chain and then to null, in console.log we can see properties and methods of objects but never its implementation, i wonder if this Object is a top-level object where all built-in are stored, is that so?
thank you all for answers!
Javascript is an object-based language. Which means that "everything" inherits from an Object, except Object itself, the top of the prototype chain is null. When you declare an array, you can use .lenght because that is defined in the prototype attribute. There are some exceptions, such as int, floats, etc.
In fact, when you run a console, you can see every method from its parents. Try creating a Constructor for a new object, let's say, for example:
var Person = function(name, yearOfBirth, job) {
this.name = name;
this.yearOfBirth = yearOfBirth;
this.job = job;
}
Add a method to this Person's prototype:
Person.prototype.calculateAge = function() {
console.log(2016 - this.yearOfBirth);
};
And create an instance of this new "class":
var sarah = new Person('Sarah', 1994, 'programmer');
Now go to the console on your browser and type sarah to see it's methods and variables. You'll notice that variables that are exclusive for John will appear on the first drop-down, but you'll also notice that John does not have calculateAge attached directly to it. That's because it's in its prototype, search for something called _proto_ and you'll see this method and also a construct and guess what else? Another _proto_, this time, it's the proto from Person's parent, or, in other words, the Object.
There you can find most of the methods from the Object itself.
Prototypes are what makes inheritance possible in javascript
Perhaps you want to look at ECMA specifications?
http://www.ecma-international.org/publications/standards/Ecma-262.htm
Each browser may have its own implementation of Javascript. Maybe something like this repo will help.
https://github.com/v8/v8
There are two piece of javascript code samples.
1-st code is:
var child1 = new Parent1();
child1.prototype = Object.create(Parent1);
2-nd code is:
var child2 = new Parent2();
child2.prototype = Object.create(Parent2.prototype);
So could you tell me please, what is the difference between them?
First of all - .prototype - is a property of constructor (e.g. function) not object. It means, all objects created with your constructor will have constructor prototype property as prototype :)
To set prototype of created object, you can use __proto__ property or Object.setPrototypeOf method, but be aware, that both are part of new ES2015 standard, which is not fully supported by all browsers.
Your code corrected will look like this
var child1 = new Parent1();
Object.setPrototypeOf(child1, Object.create(Parent1));
//vs
var child2 = new Parent2();
Object.setPrototypeOf(child2, Object.create(Parent2.prototype));
Difference is in 1st case you're setting Function object as prototype, which is kind of meaningless because it will contain only default function object porperties. In second one you're setting constructor's prototype as a prototype of constructed object, which is meaningless too, because it's already set after constructing.
Main part: code you've posted is not really correct and not commonly used. Prototypal inheritance should be used different. To know how, you can read answers from here
Interesting part: your code is still valid javascript, but it will do not work as you expected, because it's not working with prototypal inheritance.
I just finished reading this very good article on prototypal inheritance in JavaScript, but was surprised to see how vehemently the author was against having properties defined in prototypes.
A common mistake when creating objects for the prototype chain, from programmers that come from classical OOP anyway, is to define common properties high up in the chain because they exist for all instances. We feel the need to define the property as if the abstract object described an interface. Yet there is no point in defining in a prototype a property that will be present in objects that descend from it. Javascript is not like Java : you don't declare in the base objects variables that will be different to all instances of the descendants. You declare a variable only on the level where it will be defined.
Take the name property of our animals. Since every animal has a name, it's natural to consider this property as common to all, and define it in the common denominator which is the Animal prototype. The thing is, Animal has no name. A Dog instance has a name.
In Javascript, you cannot say an Animal has a name. Animal is an object, not a definition, even if we use it like so. And that object has no name property. Why then is name referred to in Animal's methods if Animal has no name? Because Animal is abstract : it is not intended to be used by itself. this, in Animal, will never refer to Animal. It will refer to whatever object descends from Animal, dino for example. And dino has a name.
If I have a very complex set of classes that have, dozens of properties in common. I don't see how it's better to duplicate those properties and the work that goes into setting them up on each instantiable derived class when the work can be done once in the base class, even if that base class was meant to be 'abstract'.
For instance:
function Analysis(args){
args = args || {};
// Extract supported init properties from args
this.description = args.description;
this.host = args.host;
this.source = args.source;
this.identifier = args.identifier;
this.vendor = args.vendor;
this.agent = args.agent;
//etc...
}
function PortfolioAnalysis(args){
Analysis.call(this, args);
args = args || {};
this.portfolio = args.portfolio;
this.author = args.author;
//etc...
}
PortfolioAnalysis.prototype = Object.create(Analysis.prototype);
PortfolioAnalysis.prototype.constructor = PortfolioAnalysis;
function TreatyAnalysis(args){
Analysis.call(this, args);
args = args || {};
this.treaty = args.treaty;
this.terms = args.terms;
//etc...
}
TreatyAnalysis.prototype = Object.create(Analysis.prototype);
TreatyAnalysis.prototype.constructor = TreatyAnalysis;
//etc...
So the article is saying I should paste the initialization code for the properties description, host, source, etc. in each of the derived classes, and remove it from the base class.
I don't see why that's better, especially if there's a bunch of complex common logic around constructing these objects using those shared properties, what's so bad about defining them in the base class, and if it's so bad, is there a way around it that doesn't involve code duplication or having to define a separate '.init()' method?
So the article is saying I should paste the initialization code for the properties description, host, source, etc. in each of the derived classes, and remove it from the base class.
No. Your code is perfectly fine, exactly how it should be done.
And that article is saying that the properties like description, host etc should be placed on instances (like a new ThreatAnalysis(…), or even a new Analysis(…)), but not on Analysis.prototype - just what you are doing. There are some people who would "default", e.g. empty, identifiers etc on Analysis.prototype because they want to "declare" that every Analysis instance should have an identifier. That is rubbish, as the article explains.
To share your initialisation behaviour in the Analysis constructor is fine (as the article mentions, shared functions may be placed hight in the prototype chain). There's no need to inline it and make Analysis and empty object, even if it is abstract and will never be instantiated directly.
I don't think you get prototypal inheritance yet. The author is not saying “don't put the initialization code in the base constructor”. What the author is saying is “don't put properties on the base prototype”. It's totally different.
So you are allowed to do what you are currently doing. It's totally fine. What you shouldn't do however is put default values of properties on the prototype as it might cause problems. For example consider:
function Foo() {}
Foo.prototype.data = []; // you shouldn't do this
var a = new Foo;
var b = new Foo;
a.data.push(0);
alert(JSON.stringify(b.data)); // [0]
This is why you shouldn't share properties on the prototype. We modified the value of a.data but since data is shared amongst all instances of Foo we also modified b.data. Hence an invariant was violated.
Think about it like this:
The properties defined on the current object are its public properties. They are not shared.
The properties defined on the prototype of the current object are static properties. They are shared amongst the instances of the prototype.
Hence it's alright to define static properties, like the count of all the instances, on the prototype. However it's not alright to define public properties on the prototype because it may cause problems like the one above.
Dr. Alex Rauschmayer explains this better: http://www.2ality.com/2013/09/data-in-prototypes.html
Your code is fine because the this in your constructors always point to the current object, not the prototype. Hence you are not defining any properties on the prototype.
I think you got confused because of constructors vs prototypes. Perhaps this blog post would elucidate your doubts: http://aaditmshah.github.io/why-prototypal-inheritance-matters/
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How does JavaScript .prototype work?
Coming from Java background, I'm trying to understand javascript.
Please let me know if these are right.
Like in java, there is a supreme Object, from which all other objects inherit.
The prototype property is like a pointer that points to the parent object (classes in java)
For "Object" object, the prototype is null.
prototype property's values are strings denoting the objects nomenclature and aren't pointers like in C. The pointer concept is implemented using the hidden attribute,[[PROTOTYPE]] that is not accessible in script.
I'm using node.js instead of browser to learn JS.
I tried,
var human = Object.create(null); // same as var human;
console.log(Object.getPrototypeOf(human)); //null
var man = Object.create(human);
console.log(Object.getPrototypeOf(man));
//{}
//expected 'human'
var person = Object.create(Object.prototype); // same as var person = {}
console.log(Object.getPrototypeOf(person));
//{}
//expected 'object'
Yes, with the exception of Object.create(null) which doesn't inherit from anything.
Not sure what "parent object" is, but the prototype of a function consists of functions that are accessible by instances. Because Object.prototype.toString exists, so does {}.toString - it is inherited.
Yes, the prototype chain ends here.
No, .prototype (or the value returned by Object.getPrototypeOf) is an object which you can extend with properties, so that those properties are inherited by instances.
Your examples:
var human = Object.create(null); // same as var human;
No, it creates an empty object that doesn't inherit from anything. var human; sets human to undefined - which is not an object but a primitive value (not everything is an object in JavaScript).
var man = Object.create(human);
console.log(Object.getPrototypeOf(man));
//{}
//expected 'human'
Object.getPrototypeOf(man) returns the object human. This is an empty object; node shows it as {}. It is not a string. In fact, objects can be assigned to multiple variables. An object does not have a variable bound to it, so by design it is not possible at all to get a string. To check equality, you could do Object.getPrototypeOf(man) === human which yields true.
var person = Object.create(Object.prototype); // same as var person = {}
console.log(Object.getPrototypeOf(person));
//{}
//expected 'object'
This is indeed the same as {} - an empty object that inherits directly from Object. As said above, the prototype is Object.prototype and not a string. It looks empty but that's because Object.prototype's functions are available by default and hidden.
First, you need to read about the create method before you attempt to use it.
Second, invoke objects in the following manner:
var human = {}, man = {}, person = {};
Third, this is not Java. In Java classes and an understanding of classes are pretty important. Prototypes are completely optional in JavaScript. The only performance benefit offered by prototypes comes when you code makes heavy reuse of functions by reference in different scopes. If this style of coding does not apply to you then you likely do not need to use prototypes.
Prototypes are inherited from the parent, or some ancestor, object. A JavaScript parser will first look for local assignments and if none exist will then examine the current object's prototype for the presence of the named reference. In a nutshell this all you need to know at this early point in writing JavaScript.
Firstly, IMO, don't start off with node. Node is going to drag you into a pool of quicksand if you don't know javascript yet. Secondly, when I started off with function based code, then OO and only then I moved to prototypes. But if you really want to know, this question has already been answered over here. Feel free to ask me if you have any more questions!
I was reading a JavaScript book and as I was reading a chapter about inheritance, I wondered if every time you create a instance of a object is it really inheritance that happens, especially since they are similar.
Examples: Prototype Chaining - someObject.prototype = new someOtherObject();
Instantiating an object var myArray = new Array()
Similar right?
Additionally the book(JavaScript for web developers, By:Nicholas Zakas) stated, a link exists between an instance and a constructor's prototype.(The link is usually known as __proto__)
So can one argue that instantiating a object is like inheritance?
No. You are confused because one of the methods of accomplishing inheritance (viz. prototypal inheritance) takes, as the prototype, a given object. And one method of acquiring objects is via constructors.
In other words, there is nothing special about someObject.prototype = new SomeOtherObject(). You could just as well do
someObject.prototype = { myObjectLiteral: "isJustAsGoodAsYourConstructoredInstance" };
or even someObject.prototype = false. The two operations, very literally, have nothing to do with each other.
EDIT To address the quote OP found in his book:
"Recall the relationship between
constructors, prototypes, and
instances: each constructor has a
prototype object that points back to
the constructor, and instances have an
internal pointer to the prototype."
Unfortunately, this is misleading at best, and plain wrong at worst. Consider what "each constructor has a prototype object that points back to the constructor" would mean. It would mean that, for example,
Array.prototype === Array
Function.prototype === Function
function CustomConstructor() { }
CustomConstructor.prototype === CustomConstructor
But all of these statements output false if you type them in your console.
Now consider "instances have an internal pointer to the prototype." This is true, but does not fit with the terminology he is using. What this means is:
var myProto = {};
function CustomConstructor() { }
CustomConstructor.prototype = myProto; // inherit from the myProto object.
var x = new CustomConstructor(); // x is an "instance"
Object.getPrototypeOf(x) === myProto; // Object.getPrototypeOf fetches the "internal pointer"
But as you can see, I didn't inherit using a constructor, as I tried to point out above. Instantiating an object is not inheritance in any way. It is simply that the prototype property of constructors is how prototypal inheritance is accomplished, just like classical inheritance is accomplished by placing extends BaseClass after a class definition in Java. In turn, this difference is present because prototypal inheritance lets you inherit a prototype object instance, not a base class.
The JavaScript Garden has a few good sections that might help if you want more information. Relevant are "The Prototype," "Constructors," and "The instanceof Operator."
Without using objectType.inherits() if I got your concept of inheritance then it is if you take just the right part:
new someOtherObject()
This is instantiation of someOtherObject.
What happens here is you are creating an object of type someOtherObject that inherits structure from an empty object (provided you have not set a prototype on this). Because the default value of prototype is an empty object {}.
You are putting this object as prototype of someObject:
someObject.prototype = new someOtherObject();
Now if you create an instance of someObject this will happen:
You create someObject that inherits the structure of someOtherObject in that point in time, which inherits the structure of (empty)object when it has been instanced.
Maybe a better explanation is saying that the instance gets extended with the structure of its prototype at that point in time, and leave all of the inheriting wording aside, since just using that can mislead. Since classic inheritance uses base classes (types) and proto uses instances of those. Now it all depends on how you look at inheritance, which to argue about does not exists at all in javascript (object types are mutable at runtime) as by Karl Marx argues that real communism is not possible at all in the concept. If you agree with this last statement then comparing and asking about it does not matter, because neithercase is.