I'm want to understand how and why are prototypes useful in Javascript. After I thought I knew what was going on, I stumbled with the fact that a prototype is just an object and can't be "shared" by many objects in the way I thought. Let me elaborate with an example:
var SpritePrototype = {
img: null,
pos_x: 0,
pos_y: 0,
draw: function(context2d) {
/*do stuff with the canvas
using "this" to refer to
the object this method is
being called on*/
},
//Some more member functions...
}
From the concept of "objects inheriting from objects" that's usually advocated with prototype-friendly Javascript, I thought I could just do:
var player = Object.create(SpritePrototype);
But it turns out this approach is flawed, because the non-function fields will be the ones from SpritePrototype, as player's prototype is exactly SpritePrototype. That means I can't create more objects from that prototype or the non-function fields will get all mixed up.
So what's the point in Object.create, and more important, what would be the correct way to achieve what I'm trying to do? That is, how can I make "player" get a copy of the fields and inherit the functions from its prototype?
Again, I'm interested in doing things the way they're intended to be. I can always simulate inheritance by hand or skip it altogether. The point of my question is to understand prototypes and how and when they are useful, especially in my specific case.
Property values on prototypes are shared initially, but stop being shared when that property on an instance is written (assigned) to. At that moment, the instance gets its own version of the property. So it is not entirely correct to say the value is "shared". It is shared only up to the point in time at which the property on the instance is assigned to.
var SpritePrototype = {
img: 'img1'
};
var sprite1 = Object.create(SpritePrototype);
var sprite2 = Object.create(SpritePrototype);
sprite1.img = 'img2'; // does NOT affect prototype or sprite2
console.log(sprite2.img);
< "img1"
When img is referenced, its value is taken from the prototype. However, when img is written to, a new property is created on the instance to hold the new value, and used from then on.
The only way to change the property value on the prototype is to do so explicitly:
SpritePrototype.img = 'img3';
This will change img for all instances which have not yet defined their own local version of img by assigning to it.
You problem is, that you had defined "static" members in terms of object oriented design. Everything in protoype section is shared between objects. Function doesn't get actually copied to newly created object, but it is called with proper "this".
You should initialize your variables in constructor, like this
function Sprite(...) {
this.img = null;
...
}
Then for subtype you should use Object.create to create a prototype to inherit its methods, so
Player.prototype = Object.create(Sprite.prototype);
And finally, you may call parent constructor to initialize the variables
function Player(...) {
Sprite.call(this, ...);
}
PS. Constructor should be before Player.prototype assigning.
PPS. For more info see this https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Related
What sort of use cases would there be for attaching both a function AND property to your object? I haven't seen this done in Production. Is this considered bad practice?
For example:
var actor = function() {
console.log('I\'ll be back!');
}
actor.name = 'Arnold Schwarzenegger';
Function inherits from Object. Function.name is a special property that refers to the name of the function. For anonymous functions you can change the name property to whatever you want.
Is changing .name a good practice?
I would say, no. Changing the .name property doesn't achieve anything other than making things more confusing.
Your Question
attaching both a function AND property to your object
This is not what your code is doing. actor is a function, which is also an object. You can always add properties to any object except null. In fact, Function.prototype already comes with a lot of methods (properties) as defined in ES6.
Adding Properties to a Function Instance
In general, adding properties to a function instance is not a bad practice. This practice is used everywhere from creating static methods and even in jQuery (e.g. $.get):
function Person(name){
this.name = name;
}
Person.clone = function(p){
return new Person(p.name);
};
var p1 = new Person("Alex"),
p2 = Person.clone(p1); // clone p1
It is a feature of the language and you should definitely use it if it fits your purpose.
One thing I don't understand with prototypes and constructor functions..
Say I have this constructor function that will create an object with a 'name' property and a 'logName' method
function MyExample(param1){
this.name = param1;
};
MyExample.prototype.logName = function(){
console.log(this.name);
}
I understand I just added a method (logName) to the constructor function's (myExample) prototype..so that when I create a new object (me) using this constructor function that object will inherit the 'name' property and the 'logName' method. The logName method will be part of new object's (me) proto property
var me = new MyExample('bob');
me.logName(); //bob
..But why not just add the logName method to the object the constructor function is creating? (not the prototype of the constructor function) Wouldn't that provide the same result? Even though 'logName' will not be part of the new object's proto property.
function MyExample(param1){
this.name = param1;
this.logName = function(){
console.log(this.name)
};
};
var me = new MyExample('bob');
me.logName(); //bob
In your second example you recreate the logName function every time you create a new instance of MyExample. If you use the MyExample's prototype then one logName method is shared across all instances of the MyExample object and context is passed around automatically.
The prototype also allows you to add a new method at a later point that can be accessed by already-existing objects of that type or globally modify a method for all objects of a type.
This question touches on the same topic if you would like more information, Advantages of using prototype, vs defining methods straight in the constructor?
The problem with creating the method in the constructor is that it isn't being shared among all instances of MyExample.
Instead, each MyExample has it's own copy of the function. It ends up taking up more memory as the number of instances goes up and if for some reason you want to modify all MyExamples at run time, you have to change every single instances function instead of modifying the prototype.
It's the difference between everyone looking at the same "copy" of Wikipedia versus everyone saving all of Wikipedia to their hard drive and reading that. It pointlessly uses up extra hard drive space and if Wikipedia is updated, everyone is wrong until they download the new version.
If for some reason you need a function which is very similar across instances but will be slightly different for each instance (for example, using closure variables) then creating methods in the constructor may be the right way to go.
Adding methods in the prototype gives your code better abstraction.
For example, since the methods are not tied to the instances, you are able to call them on non-instances which are compatible enough. Like this:
Array.prototype.slice.call({0:'a', 1:'b', length:2}); // ["a", "b"]
If you only defined your methods on the instances, it would be necessary to create an useless instance in order to borrow the method:
[].slice.call({0:'a', 1:'b', length:2}); // ["a", "b"]
Additionally, defining a method inside th constructor means that each instance will receive a different copy.
new MyExample('bob').logName === new MyExample('bob').logName // false
This means you will waste more memory.
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::
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
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.
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.
"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.
Several things to remind;
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.
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.
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.
In the past when creating "classes" in JavaScript, I have done it like this:
function Dog(name){
this.name=name;
this.sound = function(){
return "Wuf";
};
}
However, I just saw someone do it like this instead:
var Dog = (function () {
function Dog(name) {
this.name = name;
}
Dog.prototype.sound = function () {
return "Wuf";
};
return Dog;
})();
Can you do it both ways, or is the way I've done it wrong? In that case, why? And what exactly is the difference between the two in terms of what we end up with? In both cases we can create an object by saying:
var fido = new Dog("Fido");
fido.sound();
I hope someone will enlighten me.
There are two important differences between your way and theirs.
Wrapping in a self invoking function ((function() { ... })();)
Using the .prototype property over this. for methods.
Wrapping things in a self invoking function, then assigning the result (as defined in the return statement to a variable is called the module pattern. It's a common pattern to ensure scope is more controlled.
Using Dog.prototype.sound = function() {} is preferable to this.sound = function(). The difference is that Dog.prototype.sound is defined once for all objects with the Dog constructor, and the this.sound = function() {} is defined again for each Dog object created.
The rule of thumb is: Things that are individual to an object (usually its properties) are to be defined on this, while things that are shared to all objects of the same type (usually functions) are to be defined on the prototype.
With your code, you're creating a new function sound for every new Dog instance that's being created. Javascript's prototype avoids this by creating only a single function which all object instances share; basically classical inheritance.
In the second code you're showing that's just additionally wrapped in an IIFE, which doesn't do much in this case.
The first is the traditional method of creating a constructor. The second an immediately invoked function expression that returns a constructor. This method allows you to keep variables within the module without leaking out into the global scope which could be an issue.
And what exactly is the difference between the two in terms of what we end up with?
They both, as you've seen, have the same result. The others have talked about prototype so I won't mention it here.
The second is preferable because it takes advantage of Javascript's prototypal inheritance mechanism.
Prototypes
Javascript inheritance is a cause of confusion, but it's actually fairly simple: every object has a prototype, which is an object that we will check when we try to access a property not on the original object. The prototype will, itself, have a prototype; in a simple case, like Dog, this will probably be Object.prototype.
In both of your examples, because of how the new operator works, we will end up with a prototype chain that looks like this: fido->Dog.prototype->Object.prototype. So, if we try to look for the name property on Fido, we'll find it right there on the object. If, on the other hand, we look for the hasOwnProperty property, we'll fail to find it on Fido, fail to find it on Dog.prototype, and then reach Object.prototype, where we'll find it.
In the case of sound, your examples define it in two different places: in the first case, fido and every other dog we create will have their own copy of the function. In the second case, Dog.prototype will have a single copy of the function, which will be accessed by individual dogs when the method is called. This avoids wasting resources on storing duplicates of the sound function.
It also means that we can extend the prototype chain; maybe we want a Corgi class that inherits the sound function from Dog. In the second case, we can simply ensure that Dog.prototype is in Corgi.prototype's prototype chain; in the first, we would need to create an actual Dog and put it in the prototype chain.
Having a Java-background, when I switched to Javascript, I (lazily) tried to stick with what I knew regarding oop, i.e. a classical inheritance. I'm working on a web-app (that I made), and used this kind of inheritance. However, I'm considering to change my code and rewrite the OOP parts to do prototypal inheritance (two reasons: I read a lot that it's better, and secondly, I need to try the other way in order to have a better understanding of it).
My app creates data visualisations (with D3js, but it's not the topic), and organised my code this way:
function SVGBuilder( dataset ) {
this.data = dataset;
this.xCoord;
this.startDisplaying = function() {
// stuff
this.displayElement();
}
}
The displayElement() method is defined in "classes" inheriting from SVGBuilder (which is more or less an abstract class). I then have:
function SpiralBuilder( dataset ) {
SVGBuilder.call( this, dataset );
this.displayElement = function() {
// stuff
};
}
SpiralBuilder.inheritsFrom( SVGBuilder );
I have several other "builders" based on the same structure.
The script calling the builder looks like this (it's a bit more complicated since it select the correct constructor based on the user input):
var builder = new SpiralBuilder( data );
builder.startDisplaying();
Now come the "conversion part". I read quite a lot about this, from Douglas Crockford's article, to parts of the Eloquent Javascript. In Aadit M Shah's comment, he proposes a structure which would look like this:
var svgBuilder = {
data: [],
xCoord: 0, // ?
create: function( dataset ) {
var svgBuilder= Object.create(this);
svgBuilder.data = dataset;
return svgBuilder;
},
startDisplaying: function() {
// stuff
}
}
However, at this point, I'm stuck. Firstly (technical question), can I declare variables (data, xCoord) without initialising them in this pattern? Just like a this.data;? Secondly, how am I then suppose to create the inheritances? I simply manually put the corresponding functions in the prototype? Something like:
var spiralBuilder = builder.create( dataset );
spiralBuilder.prototype.displayElements = function() {
// Code to display the elements of a spiral
};
spiralBuilder.displayElements();
If I am correct, this mean that in the script calling the builder, instead of selecting the correct constructor (which won't exist anymore), I'll have to add/modify the methods in the prototype of a single builder instance. Is it how things should be done?
Or should I try to design my code in a completly different way? If it's the case, could you give me some advices/references for this?
can I declare variables (data, xCoord) without initialising them in this pattern?
var svgBuilder = {
//removed data here as it's only going to shadowed
// on creation, defaults on prototype can be done
// if value is immutable and it's usually not shadowed later
create: function( dataset, xcoord ) {
var svgBuilder= Object.create(this);
svgBuilder.data = dataset;//instance variable
svgBuilder.xcoord = xcoord;//instance variable
return svgBuilder;
},
startDisplaying: function() {
// stuff
},
constructor : svgBuilder.create
};
I know I rarely do this in my samples but when creating instances or invoking functions it is generally better to pass parameter objects.
At some point in time you may change things here or there and you don't want to change many places in your code.
In the first couple of examples you're not using prototype at all. Every member is declared as this.something in the constructor function so is an instance specific member.
A builder can be used but when you're comfortable declaring your constructor functions, your prototype, mix ins and maybe statics then all you need is a helper function for inheritance and mix ins.
Introduction to prototype can be found here. It also goes into inheritance, mix ins, overriding, calling super and the this variable. A copy of the introduction follows:
Constructor function introduction
You can use a function as a constructor to create objects, if the constructor function is named Person then the object(s) created with that constructor are instances of Person.
var Person = function(name){
this.name = name;
};
Person.prototype.walk=function(){
this.step().step().step();
};
var bob = new Person("Bob");
Person is the constructor function as it's an object (as most anything else in JavaScript) you can give it properties as well like: Person.static="something" this is good for static members related to Person like:
Person.HOMETOWN=22;
var ben = new Person("Ben");
ben.set(Person.HOMETOWN,"NY");
ben.get(Person.HOMETOWN);//generic get function what do you thing it'll get for ben?
ben.get(22);//maybe gets the same thing but difficult to guess
When you crate an instance using Person you have to use the new keyword:
var bob = new Person("Bob");console.log(bob.name);//=Bob
var ben = new Person("Ben");console.log(bob.name);//=Ben
The property/member name is instance specific, it's different for bob and ben
The member walk is shared for all instances bob and ben are instances of Person so they share the walk member (bob.walk===ben.walk).
bob.walk();ben.walk();
Because walk() could not be found on bob direcly JavaScript will look for it in the Person.prototype as this is the constructor of bob. If it can't be found there it'll look on Function.prototype because Person's constructor is Function. Function's constructor is Object so the last thing it will look is on Object.prototype. This is called the prototype chain.
Even though bob, ben and all other created Person instances share walk the function will behave differently per instance because in the walk function it uses this. The value of this will be the invoking object; for now let's say it's the current instance so for bob.walk() "this" will be bob. (more on "this" and the invoking object later).
If ben was waiting for a red light and and bob was at a green light; then you'll invoke walk() on both ben and bob obviously something different would happen to ben and bob.
Shadowing members happens when we do something like ben.walk=22, even though bob and ben share walk the assignment of 22 to ben.walk will not affect bob.walk. This is because that statement will create a member called walk on ben directly and assign it a value of 22. There will be 2 different walk members: ben.walk and Person.prototype.walk.
When asking for bob.walk you'll get the Person.prototype.walk function because walk could not be found on bob. Asking for ben.walk however will get you the value 22 because the member walk has been created on ben and since JavaScript found walk on ben it will not look in the Person.prototype.
So assignment of a member would cause JavaScript to not look it up in the prototype chain and assign value to that. Instead it would assign the value to an already existing member of the object instance or create it and then assign he value to it.
The next part (More about prototype) will explain this with sample code and demonstrate how to inherit.