0

I'm studying prototypes right now and think that I'm almost there but I'm a bit confused on one topic.

Let's say we have:

function Animal(name, gender) {
  this.name = name;
  this.gender = gender;
}

function Cat(species) {
  this.species = species;
}

Cat.prototype.color = null;

Cat.prototype = new Animal();

My question is, why is prototype needed at all for new properties?

Why couldn't we do:

Cat.color = null;

EDIT::

JS Heirarchy map

All the white blocks are from a uml diagram from another SO post. I added the orange boxes to suit this example that I've provided. Does this diagram I've added to still make sense?

My main problem I believe is that I was making the function constructors and the actual prototype objects too similar when in fact they're completely different things. One's a function and one's an object.

EDIT 2

Revised Diagram 2

With this diagram, I'm trying to clarify how the constructor property interacts and what it is exactly connected to, and more specifically, how it affects the use of this. Any comments on the validity would help.

inthenameofmusik
  • 617
  • 1
  • 4
  • 16
  • 1
    *"why is `prototype` needed at all for new properties"* Is this a question about why Brendan Eich decided to design the language in this way (which can only be answered by him), or is this purely a technical question (in the sense of "what is the difference between `Cat` and `Cat.prototype`") ? – Felix Kling Apr 26 '16 at 21:19
  • By the way, if you wanted ```Cat``` to be an extension of ```Animal```, the proper way to do it is: ```Cat.prototype = Object.create(Animal.prototype); Animal.prototype.constructor = Animal;``` – vtange Apr 26 '16 at 22:01
  • @vtange what would be the benefit of what you just explained as opposed to just having `Cat.prototype = new Animal()`: which is having the `Cat` prototype be an instance of `Animal`. (I think I said that correct) – inthenameofmusik Apr 26 '16 at 22:24
  • 1
    If you did it your way, you're saying "all Cats should be the same as (create a new Animal with no inputs)." That means Cats are the same as Animals with no name and gender. See more at http://stackoverflow.com/questions/16100471/prototype-copy-vs-object-create-vs-new – vtange Apr 26 '16 at 22:26
  • @vtange I read the SO post and understand why you suggest that now, but why are you using `Animal.prototype.constructor = Animal`. From my reading, I thought you would need to instead do it to `Cat` as such: `Cat.prototype.constructor = Cat`. This way, `this` context will refer to the `Cat` constructor and not the `Animal` constructor. Is this correct? – inthenameofmusik Apr 26 '16 at 22:40
  • Yep. When you do ```Cat.prototype = Object.create(Animal.prototype)```, ```Cat```'s constructor will become ```Animal``` unless you set it back :) – vtange Apr 26 '16 at 22:43
  • @vtange ah thank you! and when you say `Cat`'s constructor, is there a `constructor` value that I'm unaware of or is it just a reference to the actual `Cat` constructor function? – inthenameofmusik Apr 26 '16 at 22:51
  • See also [Benefits of using `Object.create` for inheritance](http://stackoverflow.com/q/17392857/218196) – Felix Kling Apr 26 '16 at 22:51
  • Regarding the diagram, `Function -> Animal -> Cat` is not correct (no matter the direction of the arrays. There is no relationship between `Animal` and `Cat`. – Felix Kling Apr 26 '16 at 22:52
  • the ```constructor``` values are a property of the ```prototype``` object. That's why you re-set it via ```blah.prototype.constructor```. Since it's all about the data within the ```prototypes```, it's why if you replace A's ```prototype``` with ```B.prototype```, A will think its ```constructor``` is now ```function B```. – vtange Apr 26 '16 at 22:54
  • @FelixKling thank you for saying that. I was questioning that relationship. Could you look at my new diagram and see if that makes sense now? I'm trying to make sure I've got the `constructor` concept down. – inthenameofmusik Apr 26 '16 at 23:10
  • @vtange Thank you for clarifying. Would you mind also checking to see if the diagram correctly reflects what's going on? – inthenameofmusik Apr 26 '16 at 23:10
  • Yep, the diagram pretty much sums it up. You can assume Animal gets a bluebox 'Animal.prototype.constructor' as well. If you're ever not sure, just console.log(new Cat().__proto__) and/or console.log(new Cat().constructor) – vtange Apr 26 '16 at 23:31

3 Answers3

2

Good question:

Cat.color = null; sets the color only on that one Cat, if you put it on the prototype any 'Cat' you instantiate afterwards will also contain a color property.

Lets say you have something like var tabby = new Cat('feline') with the code above it without the prototype tabby won't have a color.

omarjmh
  • 13,632
  • 6
  • 34
  • 42
  • 2
    To be precise, `Cat.color = null;` sets the `color` property on the `Cat` function object. Not on an instance of Cat, nor on the Cat prototype. – RJM Apr 26 '16 at 21:10
  • @JordanHendrix, thank you. I guess what I'm interested in is why JavaScript is designed to need prototypes. Why couldn't the actual `Cat` function object be the basis of inheritance and instantiation, instead of some abstract _prototype_ concept? – inthenameofmusik Apr 26 '16 at 21:11
  • @inthenameofmusik: That would imply that every instance of `Cat` is a function because `Cat` is a function. – Felix Kling Apr 26 '16 at 21:17
  • I get it, its kinda silly to people but its just the way js does it : http://javascriptissexy.com/javascript-prototype-in-plain-detailed-language/ – omarjmh Apr 26 '16 at 21:17
  • @inthenameofmusik: *"instead of some abstract *prototype* concept"* Note that JavaScript isn't the only language which uses prototypes, although the majority of the others are not popular. https://en.wikipedia.org/wiki/Prototype-based_programming – Felix Kling Apr 26 '16 at 21:22
  • *"any 'Cat' you instantiate afterwards"* It doesn't matter when the instance was created. If an property is added to the prototype, every object having that prototype has that property. – Felix Kling Apr 26 '16 at 21:23
  • @FelixKling would you mind describing your answer in more depth? _"That would imply that every instance of `Cat` is a function because `Cat` is a function..."_. I feel like you're saying something that I need to hear but I'm just not there yet. – inthenameofmusik Apr 26 '16 at 21:25
  • @inthenameofmusik: It seems you are suggesting to use `Cat` as the prototype of every instance, not `Cat.prototype`. Since `Cat` is a function and every instance has now a function in their prototype chain, every instance is, conceptually, a function. Or are you challenging the concept of "prototypes" as a whole? What would be the alternative? – Felix Kling Apr 26 '16 at 21:33
  • I guess the alternative would be a language that supports full blown class definitions. – RJM Apr 26 '16 at 21:36
1

Several things to remind;

  1. A constructor function is used to instantiate new objects. If a constructor function has variables defined with a preceding this. keyword then those will be the properties of the instantiated object. The variables defined with preceding var keyword won't be a part of the instantiated object.
  2. The objects instantiated by a constructor function will have their prototypes assigned to the constructor function's prototype hence they have direct access to the constructor function's prototype. This means they can use (share) the properties and functions within the constructor function's prototype as if they are their own.
  3. The prototype is very useful when you think of instantiating thousands of objects from a constructor function. The unique properties of each object such as name, id, color or whatever should be defined with a preceding this. in the constructor. Such as this.name or this.color however the functionalities that they are expected to share should be defined in the constructors prototype since obviously it would be a waste of memory to reserve a room for them within each instantiated object.
Redu
  • 25,060
  • 6
  • 56
  • 76
  • *" If an constructor function has variables defined with a preceding `this.`"* Technically, those are not variables anyway. – Felix Kling Apr 26 '16 at 21:30
  • @Redu Thank you, this really helped me get closer to understanding. So... the reason of needing a function constructor _and_ a prototype instead of just having a function constructor is so that non-specific properties can be referenced down the chain, therefore saving memory instead of passing them onto each instance. Is this the only reason this constructor/prototype design is used in JavaScript or am I missing some more reasons? I'm wondering if the tradeoff of all this complexity is worth the performance gain. – inthenameofmusik Apr 26 '16 at 21:36
  • @inthenameofmusik: *"the tradeoff of all this complexity"* I'd argue that prototypes are not much more complex than variable scope, which basically works in a very similar way. A very practical advantage of prototypes are polyfills: You can easily add support for new APIs by extending the respective prototype (e.g. `Array.prototype`) without any impact on the user / creator of arrays. – Felix Kling Apr 26 '16 at 21:38
  • @inthenameofmusik you got it right.. that's how it is. Imagine all the array and object functions those reside in the Array.prototype or Object.prototype which are directly accessible from all the objects and arrays instantiated in your code. What else could be better? – Redu Apr 26 '16 at 21:42
  • @Redu thank you. I'm getting it now. Would you mind commenting on the diagram I posted above, whether it's a correct representation of what's going on. I believe I should have `animal.prototype` pointing to a new `object.prototype` box but I'm not sure what the `object.prototype` box should be pointing to. – inthenameofmusik Apr 26 '16 at 22:22
  • @inthenameofmusik there is only one Object.prototype and in JS everything eventually emerges to it. That chart is perfect. – Redu Apr 26 '16 at 22:28
1

"Everything in Javascript is an Object"

Ever wonder why you can use .toString() on say the number 50 without having written Number.toString = function(){....?

Because it's built-into Javascript. All Number.prototypes have the .toString method. And the list goes on for Arrays, Objects, Strings, etc.

Every time you write a Number in JS, imagine (no constructor is really called) calling a Number constructor function similar to your constructor functions for Animal & Cat.

That's what constructor functions do. They create an instance of (theirname).prototype. That's why function Animal() makes something of Animal.prototype and so on.

function Animal() and function Cat() otherwise have nothing to do with Animal.prototype and Cat.prototype. If you actually made a new Animal with new Animal() and then changed the constructor, the new Animal you just made wouldn't be updated, because it was constructed before the constructor changed.

Let's say you make a Cat "Hobbes"

Then after that, when you say Cat.prototype.color = null;, you're saying all objects of Cat.prototype should have a 'null' value for color. This will update Cats you constructed before, since now when you try to find Hobbe's color, it will spit undefined since you didn't give a color to Hobbes himself in function Cat(), but then JS will backtrack to Hobbe's Cat.prototype and find that color is actually null.

Hope that helps.

vtange
  • 649
  • 1
  • 5
  • 10
  • 2
    FWIW, primitive values are not objects. No constructor is called. Only when a primitive value is accessed *like* an object (property access), it is temporarily converted to an object. – Felix Kling Apr 26 '16 at 21:32
  • Yep, correct. This is just an analogy I made to help OP understand why we work with prototypes. – vtange Apr 26 '16 at 21:51
  • @FelixKling would you mind looking at the edit I made with the diagram and let me know if my way of diagramming this makes sense? – inthenameofmusik Apr 26 '16 at 21:58
  • @vtange if you wouldn't mind commenting on the correctness of the diagram I uploaded at the top. Thank you. – inthenameofmusik Apr 26 '16 at 21:58