I've been spending my past couple hours researching prototypical inheritance, but I'm left with conflicting/unclear answers on how it should be done. It seems to be working for how I'm using it right now.
Paper.Component = function(){}; // useless unless I can solve problem mentioned below?
Paper.Component.prototype = {
isInBounds: function(x, y){
// ...
},
setPosition: function(x, y){
// ...
},
setSize: function(w, h){
// ...
}
};
Paper.Button = function(x, y, w, h, text){
// ...
}
Paper.Button.prototype = Object.create(Paper.Component.prototype);
It also seems to have another problem; How can I have Paper.Button save it's constructor information (x,y,w,h) onto Paper.Component rather than on itself? I.e., how can every child of Paper.Component inherit and set these values?
What you have so far is fine. The missing bit in Button looks like this:
Paper.Button = function(x, y, w, h, text){
Paper.Component.call(this, /*...any args required by it...*/);
// Button stuff here...
};
Function#call calls a function with a specific this value and any arguments you pass it. So the above calls Paper.Component from within Paper.Button with this referencing the current object, and passing along any appropriate arguments.
You also want to set the constructor property on any prototype you replace rather than just adding to. It's largely optional (JavaScript itself doesn't use constructor for anything), but since the JavaScript engine sets it on the default prototype objects, we should set it when replacing them, just so we're consistent with the default prototypes.
Slightly simpler, concrete example:
function Shape(sides, color) {
this.sides = sides;
this.color = color;
}
// Shape.prototype.method = ...
function Rectangle(color) {
Shape.call(this, 4, color);
// Rectangle stuff here...
}
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle; // <== The constructor bit
// Rectangle.prototype.method = ...
If you're interested in setting up hierarchies of "classes" of objects using prototypical inheritance in JavaScript, you might want to look at my Lineage helper script, which automates the above with a simpler syntax and provides other useful features.
A good reference is the MDN - Inheritance revisited page
What you are looking for (I think) is something like this:
Paper.Component = function(x,y,w,h){
this.setPosition( x, y );
this.setSize( w, h );
};
Paper.Component.prototype.isInBounds = function(x, y){};
Paper.Component.prototype.setPosition = function(x, y){};
Paper.Component.prototype.setSize = function(w, h){};
Paper.Button = function(x, y, w, h, text){
Paper.Component.apply( this, arguments );
}
Paper.Button.prototype = Object.create(Paper.Component.prototype);
Paper.Button.prototype.constructor = Paper.Button;
Things to note:
Do not do Paper.Component.prototype = { ... } as it will overwrite the current prototype (e.g. the existing .prototype.constructor and anything else anyone has already created).
Remember to set the constructor (the last line).
Related
I typically implement inheritance along the following lines.
function Animal () { this.x = 0; this.y = 0;}
Animal.prototype.locate = function() {
console.log(this.x, this.y);
return this;
};
Animal.prototype.move = function(x, y) {
this.x = this.x + x;
this.y = this.y + y;
return this;
}
function Duck () {
Animal.call(this);
}
Duck.prototype = new Animal();
Duck.prototype.constructor = Duck;
Duck.prototype.speak = function () {
console.log("quack");
return this;
}
var daffy = new Duck();
daffy.move(6, 7).locate().speak();
I've read this post by Eric Elliott and if I understand correctly I can use Object.create and Object.assign instead? Is it really that simple?
var animal = {
x : 0,
y : 0,
locate : function () {
console.log(this.x, this.y);
return this;
},
move : function (x, y) {
this.x = this.x + x;
this.y = this.y + y;
return this;
}
}
var duck = function () {
return Object.assign(Object.create(animal), {
speak : function () {
console.log("quack");
return this;
}
});
}
var daffy = duck();
daffy.move(6, 7).locate().speak();
As an aside, by convention constructor functions are capitalized, should object literals that act as constructors also be capitalized?
I realise there are many questions here discussing new versus Object.create, but they typically seem to relate to Duck.prototype = new Animal(); versus Duck.prototype = Object.create(Animal.prototype);
Yes, it is that simple. In your example with Object.create/Object.assign, you are using a factory function to create new instances of duck (similar to the way jQuery creates new instances if you select an element with var body = $('body')). An advantage of this code style is, that it doesn't force you to call a constructor of animal when you want to create a new duck instance (as opposed to ES2015 Classes).
Differences in initialization
Maybe one interesting tidbit that works slightly differently than if you were to use a constructor (or any other initialization function):
When you create a duck instace, all the properties of animal are in the [[Prototype]] slot of the duck instance.
var daffy = duck();
console.log(daffy); // Object { speak: function() }
So daffy does not have any own x and y properties yet. However, when you call the following, they will be added:
daffy.move(6, 7);
console.log(daffy); // Object { speak: function(), x: 6, y: 7 }
Why? In the function-body of animal.move, we have the following statement:
this.x = this.x + x;
So when you call this with daffy.move, this refers to daffy. So it will try to assign this.x + x to this.x. Since this.x is not yet defined, the [[Prototype]] chain of daffy is traversed down to animal, where animal.x is defined.
Thus in the first call, the this.x on the right side of the assignment refers to animal.x, because daffy.x is not defined. The second time daffy.move(1,2) is called, this.x on the right side will be daffy.x.
Alternative Syntax
Alternatively, you could also use Object.setPrototypeOf instead of Object.create/Object.assign (OLOO Style):
var duck = function () {
var duckObject = {
speak : function () {
console.log("quack");
return this;
}
};
return Object.setPrototypeOf(duckObject, animal);
}
Naming Conventions
I'm not aware of any established conventions. Kyle Simpson uses uppercase letters in OLOO, Eric Elliot seems to use lowercase. Personally I would stick with lower-case, because the object literals that act as constructors are already fully fledged objects themselves (not just blueprint, like classes would be).
Singleton
If you only wanted a single instance (e.g. for a singleton), you could just call it directly:
var duck = Object.assign(Object.create(animal), {
speak : function () {
console.log("quack");
return this;
}
});
duck.move(6, 7).locate().speak();
I've read this post by Eric Elliott and if I understand correctly I can use Object.create and Object.assign instead? Is it really that simple?
Yes, create and assign is much more simple because they're primitives, and less magic is going on - everything you do is explicit.
However, Eric's mouse example is a bit confusing, as he leaves out one step, and mixes the inheritance of mouses from animals with instantiating mouses.
Rather let's try transcribing your duckling example again - let's start with doing it literally:
const animal = {
constructor() {
this.x = 0;
this.y = 0;
return this;
},
locate() {
console.log(this.x, this.y);
return this;
},
move(x, y) {
this.x += x;
this.y += y;
return this;
}
};
const duck = Object.assign(Object.create(animal), {
constructor() {
return animal.constructor.call(this);
},
speak() {
console.log("quack");
return this;
}
});
/* alternatively:
const duck = Object.setPrototypeOf({
constructor() {
return super.constructor(); // super doesn't work with `Object.assign`
},
speak() { … }
}, animal); */
let daffy = Object.create(duck).constructor();
daffy.move(6, 7).locate().speak();
You should see that what happens here is really no different from using constructors (or class syntax for that matter), we've just stored our prototypes directly in the variables and we're doing instantiation with an explicit call to create and constructor.
Now you can figure that our duck.constructor does nothing but calling its super method, so we can actually omit it completely and let inheritance do its work:
const duck = Object.assign(Object.create(animal), {
speak() {
console.log("quack");
return this;
}
});
The other thing that is often changed is the initialisation of instance properties. There is actually no reason to initialise them if we don't really need them, it's sufficient to put some default values on the prototype:
const animal = {
x: 0,
y: 0,
locate() {
console.log(this.x, this.y);
}
};
const duck = … Object.create(animal) …;
let daffy = Object.create(duck); // no constructor call any more!
daffy.x = 5; // instance initialisation by explicit assignment
daffy.locate();
The problem with this is that it only works for primitive values, and it gets repetitive. This is where factory functions get in:
function makeDuck(x, y) {
return Object.assign(Object.create(duck), {x, y});
}
let daffy = makeDuck(5, 0);
To allow for easy inheritance, the initialisation is often not done in the factory but in a dedicated method so it can be called on "subclass" instances as well. You may call this method init, or you may call it constructor like I did above, it's basically the same.
As an aside, by convention constructor functions are capitalized, should object literals that act as constructors also be capitalized?
If you're not using any constructors, you may assign a new meaning to capitalized variable names, yes. It might however be confusing for everyone who's not accustomed to this. And btw, they're not "object literals that act as constructors", they're just prototype objects.
Rather than "inheritance" you should think about what type of "instantiation pattern" you intend to use. They have different purposes for implementation.
The top example is prototypal and the bottom, functional-shared. Check out this link: JS Instantiation patterns
Also, this is non-es6 related.
I've been spending my past couple hours researching prototypical inheritance, but I'm left with conflicting/unclear answers on how it should be done. It seems to be working for how I'm using it right now.
Paper.Component = function(){}; // useless unless I can solve problem mentioned below?
Paper.Component.prototype = {
isInBounds: function(x, y){
// ...
},
setPosition: function(x, y){
// ...
},
setSize: function(w, h){
// ...
}
};
Paper.Button = function(x, y, w, h, text){
// ...
}
Paper.Button.prototype = Object.create(Paper.Component.prototype);
It also seems to have another problem; How can I have Paper.Button save it's constructor information (x,y,w,h) onto Paper.Component rather than on itself? I.e., how can every child of Paper.Component inherit and set these values?
What you have so far is fine. The missing bit in Button looks like this:
Paper.Button = function(x, y, w, h, text){
Paper.Component.call(this, /*...any args required by it...*/);
// Button stuff here...
};
Function#call calls a function with a specific this value and any arguments you pass it. So the above calls Paper.Component from within Paper.Button with this referencing the current object, and passing along any appropriate arguments.
You also want to set the constructor property on any prototype you replace rather than just adding to. It's largely optional (JavaScript itself doesn't use constructor for anything), but since the JavaScript engine sets it on the default prototype objects, we should set it when replacing them, just so we're consistent with the default prototypes.
Slightly simpler, concrete example:
function Shape(sides, color) {
this.sides = sides;
this.color = color;
}
// Shape.prototype.method = ...
function Rectangle(color) {
Shape.call(this, 4, color);
// Rectangle stuff here...
}
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle; // <== The constructor bit
// Rectangle.prototype.method = ...
If you're interested in setting up hierarchies of "classes" of objects using prototypical inheritance in JavaScript, you might want to look at my Lineage helper script, which automates the above with a simpler syntax and provides other useful features.
A good reference is the MDN - Inheritance revisited page
What you are looking for (I think) is something like this:
Paper.Component = function(x,y,w,h){
this.setPosition( x, y );
this.setSize( w, h );
};
Paper.Component.prototype.isInBounds = function(x, y){};
Paper.Component.prototype.setPosition = function(x, y){};
Paper.Component.prototype.setSize = function(w, h){};
Paper.Button = function(x, y, w, h, text){
Paper.Component.apply( this, arguments );
}
Paper.Button.prototype = Object.create(Paper.Component.prototype);
Paper.Button.prototype.constructor = Paper.Button;
Things to note:
Do not do Paper.Component.prototype = { ... } as it will overwrite the current prototype (e.g. the existing .prototype.constructor and anything else anyone has already created).
Remember to set the constructor (the last line).
I'm learning prototype based OOP in javascript recently. After reading lots of websites explaining the prototype mechanism in javascript, I settle down with this approach from Mozilla
Here are some snippets from Mozilla:
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info("Shape moved.");
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
rect instanceof Rectangle // true.
rect instanceof Shape // true.
rect.move(1, 1); // Outputs, "Shape moved."
I understand that Rectangle.prototype = Object.create(Shape.prototype); will set the prototype chain properly so that instance of Rectangle will inherit methods from Shape.
But the Rectangle.prototype.constructor = Rectangle; line is a little bit confusing.
From this website I know that when we create a new object like function MyClass(){}, javascript will set its prototype to an empty object and the MyClass.prototype.constructor will point back to MyClass. So Rectangle.prototype.constructor = Rectangle; will fix the broken link since Object.create(Shape.prototype).constructor still points to Shape.
But if I remove this line, I will still get the following code running without a problem. I'm just feeling curious if there's any use case for the constructor attribute? What will go wrong if I just leave it pointing to the super class?
Setting a property this way will make it enumerable(a for in statement would list the constructor property).
var rec = new Rectangle();
'constructor' in rec; //true
You should be using Object.defineProperty or pass in a second argument to Object.create.
Rectangle.prototype = Object.create(Shape.prototype, {constructor: {enumerable: false }});
var rec = new Rectangle();
'constructor' in rec; //
Please refer to this thread for more on the constructor property:
https://stackoverflow.com/a/4013295
In your code it doesn't make any difference, as you still create your object using Rectangle constructor (explicitly using the name of it: var rect = new Rectangle();).
The difference is only in the case, when you've lost reference to object's constructor, or you simply don't know what is it:
// Shape - superclass
function Shape() {
this.x = 1;
this.y = 1;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info(this.x + " " + this.y);
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
//Rectangle.prototype.constructor = Rectangle;
console.info("RECT");
var rect = new Rectangle;
console.info(rect);
console.info(rect instanceof Rectangle);
console.info(rect instanceof Shape);
//console.info(rect instanceof Square); TypeError - Square must be a function, but is still not initialized
rect.move(1, 1);
console.info("RECT2");
var rect2 = new rect.constructor();
console.info(rect2);
console.info(rect2 instanceof Rectangle);
console.info(rect2 instanceof Shape);
// console.info(rect2 instanceof Square); TypeError - Square must be a function, but is still not initialized
rect2.move(1, 1);
console.info("RECT3");
var Square = rect.constructor;
Square.prototype.move = function(x, y) { // just to make it different
this.x *= x;
this.y *= y;
console.info(this.x + " " + this.y);
}
var rect3 = new Square();
console.info(rect3);
console.info(rect3 instanceof Rectangle);
console.info(rect3 instanceof Shape);
console.info(rect3 instanceof Square);
rect3.move(4, 4);
I commented out the line with setting the constructor. Uncomment it to see the difference in output. I have also added another way to use the constructor property - another inheritance example (Square based on Shape but with different move method).
So if you don't fix this link, in the case I showed, you will use Shape constructor, and creation of objects of the same type will become simply impossible. rect was of both types, but rect2 is not anymore.
So assuming, even if you don't need it, it is a very good practice to fix this link to the correct constructor, as someone working with your code in the future may need it.
EDIT:
constructor property doesn't affect anything in the object itself. It's set once object is created to save the reference to the function which created the object. If you will need it in the future than it makes sense to have a correct one. I don't see any other issues.
instanceOf will return true if the object is of the specified object type in its inheritance chain.
It has nothing to do with constructor property.
We can use constructor property if we want to initialize property or execute default code at the time of creation of Object.
For example in your example, if we want to create Rectangle always in red color, we can use constructor of Rectangle as follows:
function Rectangle() {
Shape.call(this); // call super constructor.
this.color = "red";
}
Hope this clear your query.
[Edited: vector2d and vector3d are not good examples. I am now using pt and ptMass instead.]
I have been searching answers for this but there seems no good solution available.
Suppose I have an object as below.
function pt(x,y){
this.x = x;
this.y = y;
}
Now, I would like to have a point mass as below.
function ptMass(x,y,m){
this.x = x;
this.y = y;
this.m = m;
}
It is better to use inheritance to create the ptMass class.
I am currently using the following way to do it.
function ptMass(x,y,m){
pt.apply(this.arguments);
this.m = m;
}
Is there a way of doing this with prototype?
I have tried the following but it doesn't work.
pt = function(m){this.m = m};
ptMass.prototype = new pt();
One more question, what is the advance of using prototypical inheritance?
First, why is it better to inherit it?
I get several arguments for inheritance, though I prefer composition (modules/components and dependency-injection) myself.
Second, you get reuse out of doing:
function Vector2D (x, y) {
this.x = x;
this.y = y;
}
function Vector3D (x, y, z) {
Vector2D.call(this, x, y);
this.z = z;
}
Third, in a large-scale JS app, creating tens of thousands (or millions) of polygons, the extra function calls are just going to slow it down, unnecessarily.
Fourth, you can't use
Vector3D.prototype = new Vector2D();
First, you're not initializing x and y to anything inside of the Vector2D.
Second, prototype is meant to hold STATIC properties and functions, if you're coming from other languages.
Even if you did initialize Vector2D, it would still be the exact same Vector2D for every instance of your Vector3D class.
Why not just:
var point2D = function (x, y) {
return { x : x,
y : y };
},
point3D = function (x, y, z) {
return { x : x,
y : y,
z : z };
};
...and when you have those particular building-blocks as your base elements, compose different modules which use them?
EDIT
function anObj (x, y) {
var privateFunc = function () { return y; };
return {
x : x,
method : function () { return privateFunc(); }
};
}
var myObj = anObj(1, 2);
myObj.x; // 1
myObj.method(); // 2
The upside here is that you've now got private variables (privateFunc and y).
The downside is that in terms of memory-usage, each instance of your object needs to have its OWN copy of any private methods.
So if you're making hundreds of thousands of these objects (vertices/polygons, for example), and they don't NEED to have private state, this isn't the method to use.
If, however, you're making players or enemies, or controllers, or anything which you DON'T want tampered with, then you DO want to use a method like this (or more advanced).
If you have a situation where you might want STATIC data / methods which are also 100% private, then you should try a format like this:
var objectMaker = (function () {
var staticData = 32,
staticFunc = function (num) { return num + staticData; };
return function (x, y) {
var privateData = 12,
privateMethod = function (num) {
return y + privateData + staticFunc(num);
};
return { x : x,
method : function (num) { return privateMethod(num); }
};
};
}());
var myObj = objectMaker(3, 4);
myObj.x; // 3;
myObj.method(12); // 12 + y(y === 4) + privateData + staticData;
So what we've done here is we've got an immediately-firing function (it fires as soon as its defined, and returns the value of the function to the variable).
So in our particular case, this function immediately returns the actual function that we want to use to make new instances.
The private variables and functions that are inside of the immediate function (not the constructor we return) are static, in that every instance you create will have access to the exact same functions/data inside of that closure.
The downside is that those static functions have NO access to private (or even instance-specific) data.
This means that you have to pass your values into the static function, and catch the return from the static function, as you can't rely on the static methods to modify instance values (technically, you can modify objects/arrays, directly if you pass them into the function, but otherwise you must catch return values).
Now you can have plenty of helper functions which are shared amongst instances (lower memory), while still being private and secure.
If what you want is public properties, public methods, and static methods/properties, THEN you can access them like so:
var Obj = function (x, y) {
this.x = x;
this.per_instance_method = function () { return y; };
};
Obj.prototype.staticData = { z : 32 };
Obj.prototype.staticMethod = function () { return this.x + this.staticData.z; };
var myObj = new Obj(3, 4);
myObj.staticMethod();
...just don't expect any prototype method to have access to any instance's private data (like y).
No, your first one was nearly correct:
function vector3d(x,y,z){
vector2d.apply(this, arguments);
// you might also use vector2d.apply(this, [].slice.call(arguments, 0,2));
// or simpler vector2d.call(this, x, y);
this.z=z;
}
So yes, you must call it from the constructor. Calling it once to create a prototype object would set up inheritance correctly, but create an instance of vector2D - which you neither need nor want. It might even cause harm. Have a look at the detailed answers on What is the reason [not] to use the 'new' keyword [for inheritance]?.
Is there a way of doing this with prototype?
vector3d.prototype = Object.create(vector2d.prototype);
will make the prototype object, from which all instances of vector3 inherit, inherit from the prototype object of vector2d. I'm not sure whether you need that, none of the usual twodimensional methods would apply on a threedimensional vector.
what is the advance of using prototypical inheritance?
Benefits of prototypal inheritance over classical?
What does it mean that Javascript is a prototype based language?
I try to create some class based-on jQuery style like the following code.
myClass = window.myClass = function(x, y)
{
return new myClass.fn.init(x, y);
};
myClass.fn = myClass.prototype =
{
init: function(x, y)
{
// logic for creating new myClass object.
}
};
I do not understand why jQuery use new keyword for creating its class because from my experiment, JavaScript always create myClass.init object instead of myClass object. However, I try to remove new keyword in myClass constructor. But it still not changes. Can you explain why jQuery can do that but I can’t or give me some code for using in init function?
By the way, I can use the following code instead of jQuery style code for creating the same object. What is the different between my code & jQuery code? Are there any benefit for using jQuery style?
myClass = window.myClass = function(x, y)
{
this.init(x, y);
};
myClass.fn = myClass.prototype =
{
init: function(x, y)
{
this.x = x;
this.y = y;
}
};
PS. I like write code that separate initial logic into function because it is very easy to override this function by other people that use my code like my following code.
// override init function of myClass
myClass.fn._old_init = myClass.fn.init;
myClass.fn.init = function()
{
// logic for doing something before init
this._old_init();
// logic for doing something after init
};
Thanks,
This approach should work perfectly. One thing you may be missing is the fact that, using this technique, you're not going to be creating an instance of myClass; you're going to be creating an instance of myClass.prototype.init.
So, any methods defined in myClass.prototype won't be available to the instance. You'll want to make sure that the init's prototype points to myClass's prototype:
myClass.fn.init.prototype = myClass.fn;
FWIW, I don't see any real benefit in this approach. What's wrong with this? -
function myClass(x,y) {
if ( !(this instanceof myClass) ) {
return new myClass(x,y);
}
// Prepare instance
}
myClass.prototype = { /* Methods */ };
// It can still be overwritten:
var oldMyClass = myClass;
function myClass(x,y) {
// Hack some stuff here...
return new oldMyClass(x,y);
}
The jQuery object initialization should like the following code.
jQuery = window.jQuery = window.$ = function (x, y)
{
return new jQuery.fn.init(x, y);
};
jQuery.fn = jQuery.prototype =
{
init: function()
{
// some logic for object initialization
return this;
}
};
jQuery.fn.init.prototype = jQuery.fn;
The only one benefit of this code is it always create instance of jQuery object although you do not use new keyword for creating object.
In the other hand, my code that use init function for initialization object does not work if you do not use new keyword when call it. For fixing it, you must add some code like "J-P" example for checking this object. If type of it is not current class, it will automatically create instance for it.
Both of codes should work fine. But I like jQuery style more than "J-P" style because it is quite easy to read and modify.
Thanks,
I'm not entirely sure what the question is, but I'll do my best to answer what I think you're asking.
the new keyword is used to instantiate a new instance of the object returned by the function defined by the init property on the function prototype.
I believe the code is written in this fashion so that the new keyword is not required each time you instantiate a new jQuery object and also to delegate the logic behind the object construction to the prototype. The former I believe is to make the library cleaner to use and the latter to keep the initialisation logic cleanly in one place and allow init to be recursively called to construct and return an object that correctly matches the passed arguments.
Your second block of code does not return an object. Try it out in Firebug with
var myClass1 = myClass(1,2);
console.log(myClass1);
you get this.init is not a function error. The only way that this would work is if you use the new keyword as in
var myClass1 = new myClass(1,2);
console.log(myClass1);
Compare that with the code similar to that in jQuery
myClass = window.myClass = function(x, y)
{
return new myClass.fn.init(x, y);
};
myClass.fn = myClass.prototype =
{
init: function(x, y)
{
this.x = x;
this.y = y;
}
};
var myClass1 = myClass(1,2);
console.log(myClass1);
var myClass2 = new myClass(1,2);
console.log(myClass2);
In each case, you correctly get an object returned with an x property with value 1 and a y property with value 2, whether the new keyword is used or not.