Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am trying to implement inheritance in javascript. I came up with following minimal code to support it.
function Base(){
this.call = function(handler, args){
handler.call(this, args);
}
}
Base.extend = function(child, parent){
parent.apply(child);
child.base = new parent;
child.base.child = child;
}
Experts, please let me know if this will be sufficient or any other important issue I may have missed. Based on similar issues faced please suggest other changes.
Here is complete test script:
function Base(){
this.call = function(handler, args){
handler.call(this, args);
}
this.superalert = function(){
alert('tst');
}
}
Base.extend = function(child, parent){
parent.apply(child);
child.base = new parent;
child.base.child = child;
}
function Child(){
Base.extend(this, Base);
this.width = 20;
this.height = 15;
this.a = ['s',''];
this.alert = function(){
alert(this.a.length);
alert(this.height);
}
}
function Child1(){
Base.extend(this, Child);
this.depth = 'depth';
this.height = 'h';
this.alert = function(){
alert(this.height); // display current object height
alert(this.a.length); // display parents array length
this.call(this.base.alert);
// explicit call to parent alert with current objects value
this.call(this.base.superalert);
// explicit call to grandparent, parent does not have method
this.base.alert(); // call parent without overriding values
}
}
var v = new Child1();
v.alert();
alert(v.height);
alert(v.depth);
To implement javascript inheritance in ECMAScript 5 you can define the prototype of an object and use Object.create to inherit. You can also add/override properties as much as you want.
Example:
/**
* Transform base class
*/
function Transform() {
this.type = "2d";
}
Transform.prototype.toString = function() {
return "Transform";
}
/**
* Translation class.
*/
function Translation(x, y) {
// Parent constructor
Transform.call(this);
// Public properties
this.x = x;
this.y = y;
}
// Inheritance
Translation.prototype = Object.create(Transform.prototype);
// Override
Translation.prototype.toString = function() {
return Transform.prototype.toString() + this.type + " Translation " + this.x + ":" + this.y;
}
/**
* Rotation class.
*/
function Rotation(angle) {
// Parent constructor
Transform.call(this);
// Public properties
this.angle = angle;
}
// Inheritance
Rotation.prototype = Object.create(Transform.prototype);
// Override
Rotation.prototype.toString = function() {
return Transform.prototype.toString() + this.type + " Rotation " + this.angle;
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
I think Crockfords solution is too complicated, as is John's. It's much simpler to get javascript inheritance than both of them seem to describe. Consider:
//Classes
function A() {
B.call(this);
}
function B() {
C.call(this);
this.bbb = function() {
console.log("i was inherited from b!");
}
}
function C() {
D.call(this);
}
function D() {
E.call(this);
}
function E() {
//instance property
this.id = Math.random()
}
//set up the inheritance chain (order matters)
D.prototype = new E();
C.prototype = new D();
B.prototype = new C();
A.prototype = new B();
//Add custom functions to each
A.prototype.foo = function() {
console.log("a");
};
B.prototype.bar = function() {
console.log("b");
};
C.prototype.baz = function() {
console.log("c");
};
D.prototype.wee = function() {
console.log("d");
};
E.prototype.woo = function() {
console.log("e");
};
//Some tests
a = new A();
a.foo();
a.bar();
a.baz();
a.wee();
a.woo();
console.log(a.id);
a.bbb();
console.log(a instanceof A);
console.log(a instanceof B);
console.log(a instanceof C);
console.log(a instanceof D);
console.log(a instanceof E);
var b = new B();
console.log(b.id)
I've written a complete description of the above solution on my blog.
As I played with JS objects, I found a more minimalistic solution :-) Enjoy!
function extend(b,a,t,p) { b.prototype = a; a.apply(t,p); }
Example
function A() {
this.info1 = function() {
alert("A");
}
}
function B(p1,p2) {
extend(B,A,this);
this.info2 = function() {
alert("B"+p1+p2);
}
}
function C(p) {
extend(C,B,this,["1","2"]);
this.info3 = function() {
alert("C"+p);
}
}
var c = new C("c");
c.info1(); // A
c.info2(); // B12
c.info3(); // Cc
Here is the simplest and I hope the easiest way to understand inheritance in JS. Most helpful this example will be for PHP programmers.
function Mother(){
this.canSwim = function(){
console.log('yes');
}
}
function Son(){};
Son.prototype = new Mother;
Son.prototype.canRun = function(){
console.log('yes');
}
Now the son has one overridden method and one new
function Grandson(){}
Grandson.prototype = new Son;
Grandson.prototype.canPlayPiano = function(){
console.log('yes');
};
Grandson.prototype.canSwim = function(){
console.log('no');
}
Now the grandson has two overridden methods and one new
var g = new Grandson;
g.canRun(); // => yes
g.canPlayPiano(); // => yes
g.canSwim(); // => no
Why not use objects instead of functions :
var Base = {
superalert : function() {
alert('tst');
}
};
var Child = Object.create(Base);
Child.width = 20;
Child.height = 15;
Child.a = ['s',''];
Child.childAlert = function () {
alert(this.a.length);
alert(this.height);
}
var Child1 = Object.create(Child);
Child1.depth = 'depth';
Child1.height = 'h';
Child1.alert = function () {
alert(this.height);
alert(this.a.length);
this.childAlert();
this.superalert();
};
And call it like this :
var child1 = Object.create(Child1);
child1.alert();
This approach is much more cleaner then with functions.
I found this blog explaining why inheritance with functions isn't a proper way to do it in JS : http://davidwalsh.name/javascript-objects-deconstruction
EDIT
var Child can also be written as :
var Child = Object.create(Base, {
width : {value : 20},
height : {value : 15, writable: true},
a : {value : ['s', ''], writable: true},
childAlert : {value : function () {
alert(this.a.length);
alert(this.height);
}}
});
Here's my solution, which is based on the standard prototypical inheritance method described in Lorenzo Polidori's answer.
First, I start off by defining these helper methods, which make things easier to understand and more readable later on:
Function.prototype.setSuperclass = function(target) {
// Set a custom field for keeping track of the object's 'superclass'.
this._superclass = target;
// Set the internal [[Prototype]] of instances of this object to a new object
// which inherits from the superclass's prototype.
this.prototype = Object.create(this._superclass.prototype);
// Correct the constructor attribute of this class's prototype
this.prototype.constructor = this;
};
Function.prototype.getSuperclass = function(target) {
// Easy way of finding out what a class inherits from
return this._superclass;
};
Function.prototype.callSuper = function(target, methodName, args) {
// If methodName is ommitted, call the constructor.
if (arguments.length < 3) {
return this.callSuperConstructor(arguments[0], arguments[1]);
}
// `args` is an empty array by default.
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
var method = superclass.prototype[methodName];
if (typeof method != "function") throw new TypeError("TypeError: Object " + superclass.prototype + " has no method '" + methodName + "'");
return method.apply(target, args);
};
Function.prototype.callSuperConstructor = function(target, args) {
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
return superclass.apply(target, args);
};
Now, not only can you set the superclass of a class with SubClass.setSuperclass(ParentClass), but you can also call overridden methods with SubClass.callSuper(this, 'functionName', [argument1, argument2...]):
/**
* Transform base class
*/
function Transform() {
this.type = "2d";
}
Transform.prototype.toString = function() {
return "Transform";
}
/**
* Translation class.
*/
function Translation(x, y) {
// Parent constructor
Translation.callSuper(this, arguments);
// Public properties
this.x = x;
this.y = y;
}
// Inheritance
Translation.setSuperclass(Transform);
// Override
Translation.prototype.toString = function() {
return Translation.callSuper(this, 'toString', arguments) + this.type + " Translation " + this.x + ":" + this.y;
}
/**
* Rotation class.
*/
function Rotation(angle) {
// Parent constructor
Rotation.callSuper(this, arguments);
// Public properties
this.angle = angle;
}
// Inheritance
Rotation.setSuperclass(Transform);
// Override
Rotation.prototype.toString = function() {
return Rotation.callSuper(this, 'toString', arguments) + this.type + " Rotation " + this.angle;
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
Admittedly, even with the helper functions the syntax here is pretty awkward. Thankfully though, in ECMAScript 6 some syntactic sugar (maximally minimal classes) has been added to make things much prettier. E.g.:
/**
* Transform base class
*/
class Transform {
constructor() {
this.type = "2d";
}
toString() {
return "Transform";
}
}
/**
* Translation class.
*/
class Translation extends Transform {
constructor(x, y) {
super(); // Parent constructor
// Public properties
this.x = x;
this.y = y;
}
toString() {
return super(...arguments) + this.type + " Translation " + this.x + ":" + this.y;
}
}
/**
* Rotation class.
*/
class Rotation extends Transform {
constructor(angle) {
// Parent constructor
super(...arguments);
// Public properties
this.angle = angle;
}
toString() {
return super(...arguments) + this.type + " Rotation " + this.angle;
}
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
Note that ECMAScript 6 is still in the draft stage at this point, and as far as I know is not implemented in any major web browser. However, if you wish you can use something like Traceur compiler to compile ECMAScript 6 down to the plain old ECMAScript 5-based JavaScript. You can see the above example compiled using Traceur here.
While I agree with all above answers, I feel that JavaScript need not be Object Oriented, (Avoid inheritance), instead an object-based approach should be sufficient in most cases.
I like the way Eloquent JavaScript starts its Chapter 8 on Object Oriented Programming talking about OO. Instead of deciphering best way to implement Inheritance, more energy should be devoted to learn functional aspects of JavaScript, hence, I found Chapter 6 on Functional Programming, more interesting.
//This is an example of how to override a method, while preserving access to the original.
//The pattern used is actually quite simple using JavaScripts ability to define closures:
this.somefunction = this.someFunction.override(function(args){
var result = this.inherited(args);
result += this.doSomethingElse();
return result;
});
//It is accomplished through this piece of code (courtesy of Poul Krogh):
/***************************************************************
function.override overrides a defined method with a new one,
while preserving the old method.
The old method is only accessible from the new one.
Use this.inherited() to access the old method.
***************************************************************/
Function.prototype.override = function(func)
{
var remember = this;
var f = function()
{
var save = this.inherited;
this.inherited = remember;
var result = func.apply(this, Array.prototype.slice.call(arguments));
this.inherited = save;
return result;
};
return f;
}
Basic prototypical inheritance
A simple but effective way to do inheritance in JavaScript, is to use the following two-liner :
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
That is similar to doing this :
B.prototype = new A();
The main difference between both is that the constructor of A is not run when using Object.create, which is more intuitive and more similar to class based inheritance.
You can always choose to optionally run the constructor of A when creating a new instance of B by adding adding it to the constructor of B :
function B(arg1, arg2) {
A(arg1, arg2); // This is optional
}
If you want to pass all arguments of B to A, you can also use Function.prototype.apply() :
function B() {
A.apply(this, arguments); // This is optional
}
If you want to mixin another object into the constructor chain of B, you can combine Object.create with Object.assign :
B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;
Demo
function A(name) {
this.name = name;
}
A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;
function B() {
A.apply(this, arguments);
this.street = "Downing Street 10";
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
function mixin() {
}
mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;
mixin.prototype.getProperties = function() {
return {
name: this.name,
address: this.street,
year: this.year
};
};
function C() {
B.apply(this, arguments);
this.year = "2018"
}
C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
Creating your own wrapper
If you don't like writing roughly the same two-liner throughout your code, you could write a basic wrapper function like this :
function inheritance() {
var args = Array.prototype.slice.call(arguments);
var firstArg = args.shift();
switch (args.length) {
case 0:
firstArg.prototype = Object.create(Object.prototype);
firstArg.prototype.constructor = firstArg;
break;
case 1:
firstArg.prototype = Object.create(args[0].prototype);
firstArg.prototype.constructor = firstArg;
break;
default:
for(var i = 0; i < args.length; i++) {
args[i] = args[i].prototype;
}
args[0] = Object.create(args[0]);
var secondArg = args.shift();
firstArg.prototype = Object.assign.apply(Object, args);
firstArg.prototype.constructor = firstArg;
}
}
How this wrapper works :
If you pass a one parameter, it's prototype will inherit from Object.
If you pass two parameters, the first's prototype will inherit from the second's.
If you pass more than two parameters, the first's prototype will inherit from the second's and the prototypes of other parameters will be mixed in.
Demo
function inheritance() {
var args = Array.prototype.slice.call(arguments);
var firstArg = args.shift();
switch (args.length) {
case 0:
firstArg.prototype = Object.create(Object.prototype);
firstArg.prototype.constructor = firstArg;
break;
case 1:
firstArg.prototype = Object.create(args[0].prototype);
firstArg.prototype.constructor = firstArg;
break;
default:
for(var i = 0; i < args.length; i++) {
args[i] = args[i].prototype;
}
args[0] = Object.create(args[0]);
var secondArg = args.shift();
firstArg.prototype = Object.assign.apply(Object, args);
firstArg.prototype.constructor = firstArg;
}
}
function A(name) {
this.name = name;
}
inheritance(A);
function B() {
A.apply(this, arguments);
this.street = "Downing Street 10";
}
inheritance(B, A);
function mixin() {
}
inheritance(mixin);
mixin.prototype.getProperties = function() {
return {
name: this.name,
address: this.street,
year: this.year
};
};
function C() {
B.apply(this, arguments);
this.year = "2018"
}
inheritance(C, B, mixin);
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
Note
Object.create can be safely used in every modern browser, including IE9+. Object.assign does not work in any version of IE nor some mobile browsers. It is recommended to polyfill Object.create and/or Object.assign if you want to use them and support browsers that do not implement them.
You can find a polyfill for Object.create here
and one for Object.assign here.
How about this simple approach
function Body(){
this.Eyes = 2;
this.Arms = 2;
this.Legs = 2;
this.Heart = 1;
this.Walk = function(){alert(this.FirstName + ' Is Walking')};
}
function BasePerson() {
var BaseBody = new Body(this);
BaseBody.FirstName = '';
BaseBody.LastName = '';
BaseBody.Email = '';
BaseBody.IntroduceSelf = function () { alert('Hello my name is ' + this.FirstName + ' ' + this.LastName); };
return BaseBody;
}
function Person(FirstName,LastName)
{
var PersonBuild = new BasePerson();
PersonBuild.FirstName = FirstName;
PersonBuild.LastName = LastName;
return PersonBuild;
}
var Person1 = new Person('Code', 'Master');
Person1.IntroduceSelf();
Person1.Walk();
//
// try this one:
//
// function ParentConstructor() {}
// function ChildConstructor() {}
//
// var
// SubClass = ChildConstructor.xtendz( ParentConstructor );
//
Function.prototype.xtendz = function ( SuperCtorFn ) {
return ( function( Super, _slice ) {
// 'freeze' host fn
var
baseFn = this,
SubClassCtorFn;
// define child ctor
SubClassCtorFn = function ( /* child_ctor_parameters..., parent_ctor_parameters[] */ ) {
// execute parent ctor fn on host object
// pass it last ( array ) argument as parameters
Super.apply( this, _slice.call( arguments, -1 )[0] );
// execute child ctor fn on host object
// pass remaining arguments as parameters
baseFn.apply( this, _slice.call( arguments, 0, -1 ) );
};
// establish proper prototype inheritance
// 'inherit' methods
SubClassCtorFn.prototype = new Super;
// (re)establish child ctor ( instead of Super ctor )
SubClassCtorFn.prototype.constructor = SubClassCtorFn;
// return built ctor
return SubClassCtorFn;
} ).call( this, SuperCtorFn, Array.prototype.slice );
};
// declare parent ctor
function Sup( x1, x2 ) {
this.parent_property_1 = x1;
this.parent_property_2 = x2;
}
// define some methods on parent
Sup.prototype.hello = function(){
alert(' ~ h e l l o t h e r e ~ ');
};
// declare child ctor
function Sub( x1, x2 ) {
this.child_property_1 = x1;
this.child_property_2 = x2;
}
var
SubClass = Sub.xtendz(Sup), // get 'child class' ctor
obj;
// reserve last array argument for parent ctor
obj = new SubClass( 97, 98, [99, 100] );
obj.hello();
console.log( obj );
console.log('obj instanceof SubClass -> ', obj instanceof SubClass );
console.log('obj.constructor === SubClass -> ', obj.constructor === SubClass );
console.log('obj instanceof Sup -> ', obj instanceof Sup );
console.log('obj instanceof Object -> ', obj instanceof Object );
//
// Object {parent_property_1: 99, parent_property_2: 100, child_property_1: 97, child_property_2: 98}
// obj instanceof SubClass -> true
// obj.constructor === SubClass -> true
// obj instanceof Sup -> true
// obj instanceof Object -> true
//
The easiest way to use AWeb library. Official sample:
/**
* A-class
*/
var ClassA = AWeb.class({
public : {
/**
* A-class constructor
*/
constructor : function() {
/* Private variable */
this.variable1 = "A";
this.calls = 0;
},
/**
* Function returns information about the object
*/
getInfo : function() {
this.incCalls();
return "name=" + this.variable1 + ", calls=" + this.calls;
}
},
private : {
/**
* Private function
*/
incCalls : function() {
this.calls++;
}
}
});
/**
* B-class
*/
var ClassB = AWeb.class({
extends : ClassA,
public : {
/**
* B-class constructor
*/
constructor : function() {
this.super();
/* Private variable */
this.variable1 = "B";
},
/**
* Function returns extended information about the object
*/
getLongInfo : function() {
return this.incCalls !== undefined ? "incCalls exists" : "incCalls undefined";
}
}
});
/**
* Main project function
*/
function main() {
var a = new ClassA(),
b = new ClassB();
alert(
"a.getInfo " + (a.getInfo ? "exists" : "undefined") + "\n" +
"a.getLongInfo " + (a.getLongInfo ? "exists" : "undefined") + "\n" +
"b.getInfo " + (b.getInfo ? "exists" : "undefined") + "\n" +
"b.getLongInfo " + (b.getLongInfo ? "exists" : "undefined") + "\n" +
"b.getInfo()=" + b.getInfo() + "\n" +
"b.getLongInfo()=" + b.getLongInfo()
);
}
I found a solution much easier than extend and prototyping things. Actually I don't know how efficient this is though it looks clean and functional.
var A = function (p) {
if (p == null) p = this;
p.a1 = 0;
this.a2 = 0;
var a3 = 0;
};
var B = function (p) {
if (p == null) p = this;
p.b1 = new A(this);
this.b2 = new A(this);
var b3 = new A(this);
this b4 = new A();
};
var a = new A ();
var b = new B ();
result:
a
a1 0
a2 0
b
a1 0
b1
a2 0
b2
a2 0
b4
a1 0
a2 0
practical example:
var Point = function (p) {
if (p == null) p = this;
var x = 0;
var y = 0;
p.getPoint = function () { return [x,y]; };
p.setPoint = function (_x,_y) { x = _x; y = _y; };
};
var Dimension = function (p) {
if (p == null) p = this;
var w = 0;
var h = 0;
p.getDimension = function() { return [w,h] };
p.setDimension = function(_w,_h) { w = _w; h = _h };
};
var Rect = function (p) {
if (p == null) p = this;
var dimension = new Dimension(this);
var location = new Point(this);
};
var rect = new Rect ();
rect.setDimension({w:30,h:40});
rect.setPoint({x:50,y:50});
Related
This question already has answers here:
prototype: deep scope of "this" to access instance's scope
(5 answers)
Closed 6 years ago.
Suppose I have following code:
var Model = function() {};
Model.prototype.a = function() {//do smth
Model.prototype.a.on = function() {//do smth
var m = new Model();
m.a();
m.a.on();
Now I need reference to specific object m from m.a() and m.a.on() calls.
When calling m.a(), i have this referring to m.
Is it possible to get reference to m from m.a.on() call somehow?
It's a very bad idea to do so, as it leads to very strange behaviour in some cases, but it's possible:
var Model = function(x) { this.x = x };
Object.defineProperty(Model.prototype, 'a', (function() {
var lastSelf;
function get() { return lastSelf.x }
get.on = function () { return lastSelf.x * 2 };
return { get() { lastSelf=this; return get } };
})());
var m = new Model(17);
console.log(m.a(), m.a.on());
Why? I see your answer below, trying to realize what are bad cases.
You can't pass a through the variable.
You must grant access to on immediately after getting property a of the same object:
var Model = function(x) { this.x = x };
Object.defineProperty(Model.prototype, 'a', (function() {
var lastSelf;
function get() { return lastSelf.x }
get.on = function () { return lastSelf.x * 2 };
return { get() { lastSelf=this; return get } };
})());
var m1 = new Model(1), m2 = new Model(3);
console.log(m1.a(), m2.a(), m1.a.on(), m2.a.on()); // 1 3 2 6 - ok
var a1 = m1.a, a2 = m2.a;
console.log(m1.a(), m2.a(), a1.on(), a2.on()); // 1 3 6 6 - ooops!
console.log(m1.a(), m2.a(), m1.a(), a1.on(), a2.on()); // 1 3 1 2 2 - ooops!
And the other solution, but with using __proto__. According to ES6 this solution is valid for browser enviroments and for server enviroments __proto__ have to be replaced by Object.setPrototypeOf. Be sure to check browser support and other warnings.
This solution adds 1 function and 1 object per each instance.
function Model(x) {
this.x = x;
this.a = function () { return Model.prototype.a.call(this, arguments) };
this.a.__proto__ = Object.create(Model.prototype.a);
this.a.this = this;
}
Model.prototype.a = function () { return this.x };
Model.prototype.a.on = function () { return this.this.x * 2 };
var m1 = new Model(1), m2 = new Model(3);
console.log([m1.a(), m2.a(), m1.a.on(), m2.a.on()] == "1,3,2,6");
var a1 = m1.a, a2 = m2.a;
console.log([m1.a(), m2.a(), a1.on(), a2.on()] == "1,3,2,6");
console.log([m1.a(), m2.a(), m1.a(), a1.on(), a2.on()] == "1,3,1,2,6");
You can rebind the 'grandchild' methods manually in the constructor:
bindAll = function(self, obj) {
Object.keys(obj).forEach(function(k) {
if(typeof obj[k] === 'function')
obj[k] = obj[k].bind(self);
});
}
var Model = function() {
bindAll(this, this.a);
this.x = 123;
};
Model.prototype.a = function() {}
Model.prototype.a.on = function() {
console.log(this.x);
}
var m = new Model();
m.a();
m.a.on();
A more memory-savvy way is to use an explicit pointer to the root class and consistently use this.root instead of just this in methods:
var Model = function(x) {
this.x = x;
this.model = this;
this.a = Object.create(this.a);
this.a.model = this;
};
Model.prototype.a = function() {
console.log(this.model.x);
}
Model.prototype.a.on = function() {
console.log(this.model.x);
};
var m1 = new Model(11), m2 = new Model(22);
m1.a.on();
m2.a.on();
m1.a.on();
You can't access directly the parent object from a.on. You have to define some property (e.g. parent) linked to main object before calling a.on:
var Model = function() {};
Model.prototype.a = function() {//do smth
console.log(this.i);
}
Model.prototype.a.on = function() {//do smth
console.log(this.parent.i);
}
var m = new Model();
m.i = 11;
m.a();
m.a.parent = m
m.a.on();
var RPNCalculator = function() {
this.stack = [];
this.total = 0;
this.value = function() {
return this.total;
}
this.push = function(val) {
this.stack.push(val);
}
this.pop = function() {
this.stack.pop();
}
this.process = function() {
this.val1 = this.stack.pop();
this.val2 = this.stack.pop();
this.total = 0;
}
this.plus = function() {
this.process();
this.total = this.val1 + this.val2;
this.stack.push(this.total);
}
this.minus = function() {
this.process();
this.total = this.val2 - this.val1;
this.stack.push(this.total);
}
}
How can I make the RPNCalculator object inherit array methods, without creating push and pop methods myself?
For example, if I do the following
rpnCalculator = new RPNCalculator();
rpnCalculator.push(2);
it will add the number 2 to the stack array
If you want all of the methods provided by Array maybe start with inheriting the prototype from Array using Object.create and then adding your custom functions to the new constructor prototype.
var Foo = function () {};
Foo.prototype = Object.create(Array.prototype);
Foo.prototype.process = function process() {
// `this` is the array
// Do some logic...
// returning `this` to show it is the array
return this;
}
var foo = new Foo();
foo.push(3);
foo.push(2);
foo.push(1);
document.write(
'<h3>foo</h3>' +
'<pre>' + JSON.stringify(foo, null, 4) + '</pre>' +
'<h3>foo.process()</h3>' +
'<pre>' + JSON.stringify(foo.process(), null, 4) + '</pre>'
);
You could do:
this.push = this.stack.push.bind(this.stack);
this.pop = this.stack.pop.bind(this.stack);
That would just use stack's methods instead of defining your own.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am trying to implement inheritance in javascript. I came up with following minimal code to support it.
function Base(){
this.call = function(handler, args){
handler.call(this, args);
}
}
Base.extend = function(child, parent){
parent.apply(child);
child.base = new parent;
child.base.child = child;
}
Experts, please let me know if this will be sufficient or any other important issue I may have missed. Based on similar issues faced please suggest other changes.
Here is complete test script:
function Base(){
this.call = function(handler, args){
handler.call(this, args);
}
this.superalert = function(){
alert('tst');
}
}
Base.extend = function(child, parent){
parent.apply(child);
child.base = new parent;
child.base.child = child;
}
function Child(){
Base.extend(this, Base);
this.width = 20;
this.height = 15;
this.a = ['s',''];
this.alert = function(){
alert(this.a.length);
alert(this.height);
}
}
function Child1(){
Base.extend(this, Child);
this.depth = 'depth';
this.height = 'h';
this.alert = function(){
alert(this.height); // display current object height
alert(this.a.length); // display parents array length
this.call(this.base.alert);
// explicit call to parent alert with current objects value
this.call(this.base.superalert);
// explicit call to grandparent, parent does not have method
this.base.alert(); // call parent without overriding values
}
}
var v = new Child1();
v.alert();
alert(v.height);
alert(v.depth);
To implement javascript inheritance in ECMAScript 5 you can define the prototype of an object and use Object.create to inherit. You can also add/override properties as much as you want.
Example:
/**
* Transform base class
*/
function Transform() {
this.type = "2d";
}
Transform.prototype.toString = function() {
return "Transform";
}
/**
* Translation class.
*/
function Translation(x, y) {
// Parent constructor
Transform.call(this);
// Public properties
this.x = x;
this.y = y;
}
// Inheritance
Translation.prototype = Object.create(Transform.prototype);
// Override
Translation.prototype.toString = function() {
return Transform.prototype.toString() + this.type + " Translation " + this.x + ":" + this.y;
}
/**
* Rotation class.
*/
function Rotation(angle) {
// Parent constructor
Transform.call(this);
// Public properties
this.angle = angle;
}
// Inheritance
Rotation.prototype = Object.create(Transform.prototype);
// Override
Rotation.prototype.toString = function() {
return Transform.prototype.toString() + this.type + " Rotation " + this.angle;
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
I think Crockfords solution is too complicated, as is John's. It's much simpler to get javascript inheritance than both of them seem to describe. Consider:
//Classes
function A() {
B.call(this);
}
function B() {
C.call(this);
this.bbb = function() {
console.log("i was inherited from b!");
}
}
function C() {
D.call(this);
}
function D() {
E.call(this);
}
function E() {
//instance property
this.id = Math.random()
}
//set up the inheritance chain (order matters)
D.prototype = new E();
C.prototype = new D();
B.prototype = new C();
A.prototype = new B();
//Add custom functions to each
A.prototype.foo = function() {
console.log("a");
};
B.prototype.bar = function() {
console.log("b");
};
C.prototype.baz = function() {
console.log("c");
};
D.prototype.wee = function() {
console.log("d");
};
E.prototype.woo = function() {
console.log("e");
};
//Some tests
a = new A();
a.foo();
a.bar();
a.baz();
a.wee();
a.woo();
console.log(a.id);
a.bbb();
console.log(a instanceof A);
console.log(a instanceof B);
console.log(a instanceof C);
console.log(a instanceof D);
console.log(a instanceof E);
var b = new B();
console.log(b.id)
I've written a complete description of the above solution on my blog.
As I played with JS objects, I found a more minimalistic solution :-) Enjoy!
function extend(b,a,t,p) { b.prototype = a; a.apply(t,p); }
Example
function A() {
this.info1 = function() {
alert("A");
}
}
function B(p1,p2) {
extend(B,A,this);
this.info2 = function() {
alert("B"+p1+p2);
}
}
function C(p) {
extend(C,B,this,["1","2"]);
this.info3 = function() {
alert("C"+p);
}
}
var c = new C("c");
c.info1(); // A
c.info2(); // B12
c.info3(); // Cc
Here is the simplest and I hope the easiest way to understand inheritance in JS. Most helpful this example will be for PHP programmers.
function Mother(){
this.canSwim = function(){
console.log('yes');
}
}
function Son(){};
Son.prototype = new Mother;
Son.prototype.canRun = function(){
console.log('yes');
}
Now the son has one overridden method and one new
function Grandson(){}
Grandson.prototype = new Son;
Grandson.prototype.canPlayPiano = function(){
console.log('yes');
};
Grandson.prototype.canSwim = function(){
console.log('no');
}
Now the grandson has two overridden methods and one new
var g = new Grandson;
g.canRun(); // => yes
g.canPlayPiano(); // => yes
g.canSwim(); // => no
Why not use objects instead of functions :
var Base = {
superalert : function() {
alert('tst');
}
};
var Child = Object.create(Base);
Child.width = 20;
Child.height = 15;
Child.a = ['s',''];
Child.childAlert = function () {
alert(this.a.length);
alert(this.height);
}
var Child1 = Object.create(Child);
Child1.depth = 'depth';
Child1.height = 'h';
Child1.alert = function () {
alert(this.height);
alert(this.a.length);
this.childAlert();
this.superalert();
};
And call it like this :
var child1 = Object.create(Child1);
child1.alert();
This approach is much more cleaner then with functions.
I found this blog explaining why inheritance with functions isn't a proper way to do it in JS : http://davidwalsh.name/javascript-objects-deconstruction
EDIT
var Child can also be written as :
var Child = Object.create(Base, {
width : {value : 20},
height : {value : 15, writable: true},
a : {value : ['s', ''], writable: true},
childAlert : {value : function () {
alert(this.a.length);
alert(this.height);
}}
});
Here's my solution, which is based on the standard prototypical inheritance method described in Lorenzo Polidori's answer.
First, I start off by defining these helper methods, which make things easier to understand and more readable later on:
Function.prototype.setSuperclass = function(target) {
// Set a custom field for keeping track of the object's 'superclass'.
this._superclass = target;
// Set the internal [[Prototype]] of instances of this object to a new object
// which inherits from the superclass's prototype.
this.prototype = Object.create(this._superclass.prototype);
// Correct the constructor attribute of this class's prototype
this.prototype.constructor = this;
};
Function.prototype.getSuperclass = function(target) {
// Easy way of finding out what a class inherits from
return this._superclass;
};
Function.prototype.callSuper = function(target, methodName, args) {
// If methodName is ommitted, call the constructor.
if (arguments.length < 3) {
return this.callSuperConstructor(arguments[0], arguments[1]);
}
// `args` is an empty array by default.
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
var method = superclass.prototype[methodName];
if (typeof method != "function") throw new TypeError("TypeError: Object " + superclass.prototype + " has no method '" + methodName + "'");
return method.apply(target, args);
};
Function.prototype.callSuperConstructor = function(target, args) {
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
return superclass.apply(target, args);
};
Now, not only can you set the superclass of a class with SubClass.setSuperclass(ParentClass), but you can also call overridden methods with SubClass.callSuper(this, 'functionName', [argument1, argument2...]):
/**
* Transform base class
*/
function Transform() {
this.type = "2d";
}
Transform.prototype.toString = function() {
return "Transform";
}
/**
* Translation class.
*/
function Translation(x, y) {
// Parent constructor
Translation.callSuper(this, arguments);
// Public properties
this.x = x;
this.y = y;
}
// Inheritance
Translation.setSuperclass(Transform);
// Override
Translation.prototype.toString = function() {
return Translation.callSuper(this, 'toString', arguments) + this.type + " Translation " + this.x + ":" + this.y;
}
/**
* Rotation class.
*/
function Rotation(angle) {
// Parent constructor
Rotation.callSuper(this, arguments);
// Public properties
this.angle = angle;
}
// Inheritance
Rotation.setSuperclass(Transform);
// Override
Rotation.prototype.toString = function() {
return Rotation.callSuper(this, 'toString', arguments) + this.type + " Rotation " + this.angle;
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
Admittedly, even with the helper functions the syntax here is pretty awkward. Thankfully though, in ECMAScript 6 some syntactic sugar (maximally minimal classes) has been added to make things much prettier. E.g.:
/**
* Transform base class
*/
class Transform {
constructor() {
this.type = "2d";
}
toString() {
return "Transform";
}
}
/**
* Translation class.
*/
class Translation extends Transform {
constructor(x, y) {
super(); // Parent constructor
// Public properties
this.x = x;
this.y = y;
}
toString() {
return super(...arguments) + this.type + " Translation " + this.x + ":" + this.y;
}
}
/**
* Rotation class.
*/
class Rotation extends Transform {
constructor(angle) {
// Parent constructor
super(...arguments);
// Public properties
this.angle = angle;
}
toString() {
return super(...arguments) + this.type + " Rotation " + this.angle;
}
}
// Tests
translation = new Translation(10, 15);
console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false
console.log(translation.toString()) // Transform2d Translation 10:15
Note that ECMAScript 6 is still in the draft stage at this point, and as far as I know is not implemented in any major web browser. However, if you wish you can use something like Traceur compiler to compile ECMAScript 6 down to the plain old ECMAScript 5-based JavaScript. You can see the above example compiled using Traceur here.
While I agree with all above answers, I feel that JavaScript need not be Object Oriented, (Avoid inheritance), instead an object-based approach should be sufficient in most cases.
I like the way Eloquent JavaScript starts its Chapter 8 on Object Oriented Programming talking about OO. Instead of deciphering best way to implement Inheritance, more energy should be devoted to learn functional aspects of JavaScript, hence, I found Chapter 6 on Functional Programming, more interesting.
//This is an example of how to override a method, while preserving access to the original.
//The pattern used is actually quite simple using JavaScripts ability to define closures:
this.somefunction = this.someFunction.override(function(args){
var result = this.inherited(args);
result += this.doSomethingElse();
return result;
});
//It is accomplished through this piece of code (courtesy of Poul Krogh):
/***************************************************************
function.override overrides a defined method with a new one,
while preserving the old method.
The old method is only accessible from the new one.
Use this.inherited() to access the old method.
***************************************************************/
Function.prototype.override = function(func)
{
var remember = this;
var f = function()
{
var save = this.inherited;
this.inherited = remember;
var result = func.apply(this, Array.prototype.slice.call(arguments));
this.inherited = save;
return result;
};
return f;
}
Basic prototypical inheritance
A simple but effective way to do inheritance in JavaScript, is to use the following two-liner :
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
That is similar to doing this :
B.prototype = new A();
The main difference between both is that the constructor of A is not run when using Object.create, which is more intuitive and more similar to class based inheritance.
You can always choose to optionally run the constructor of A when creating a new instance of B by adding adding it to the constructor of B :
function B(arg1, arg2) {
A(arg1, arg2); // This is optional
}
If you want to pass all arguments of B to A, you can also use Function.prototype.apply() :
function B() {
A.apply(this, arguments); // This is optional
}
If you want to mixin another object into the constructor chain of B, you can combine Object.create with Object.assign :
B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;
Demo
function A(name) {
this.name = name;
}
A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;
function B() {
A.apply(this, arguments);
this.street = "Downing Street 10";
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
function mixin() {
}
mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;
mixin.prototype.getProperties = function() {
return {
name: this.name,
address: this.street,
year: this.year
};
};
function C() {
B.apply(this, arguments);
this.year = "2018"
}
C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
Creating your own wrapper
If you don't like writing roughly the same two-liner throughout your code, you could write a basic wrapper function like this :
function inheritance() {
var args = Array.prototype.slice.call(arguments);
var firstArg = args.shift();
switch (args.length) {
case 0:
firstArg.prototype = Object.create(Object.prototype);
firstArg.prototype.constructor = firstArg;
break;
case 1:
firstArg.prototype = Object.create(args[0].prototype);
firstArg.prototype.constructor = firstArg;
break;
default:
for(var i = 0; i < args.length; i++) {
args[i] = args[i].prototype;
}
args[0] = Object.create(args[0]);
var secondArg = args.shift();
firstArg.prototype = Object.assign.apply(Object, args);
firstArg.prototype.constructor = firstArg;
}
}
How this wrapper works :
If you pass a one parameter, it's prototype will inherit from Object.
If you pass two parameters, the first's prototype will inherit from the second's.
If you pass more than two parameters, the first's prototype will inherit from the second's and the prototypes of other parameters will be mixed in.
Demo
function inheritance() {
var args = Array.prototype.slice.call(arguments);
var firstArg = args.shift();
switch (args.length) {
case 0:
firstArg.prototype = Object.create(Object.prototype);
firstArg.prototype.constructor = firstArg;
break;
case 1:
firstArg.prototype = Object.create(args[0].prototype);
firstArg.prototype.constructor = firstArg;
break;
default:
for(var i = 0; i < args.length; i++) {
args[i] = args[i].prototype;
}
args[0] = Object.create(args[0]);
var secondArg = args.shift();
firstArg.prototype = Object.assign.apply(Object, args);
firstArg.prototype.constructor = firstArg;
}
}
function A(name) {
this.name = name;
}
inheritance(A);
function B() {
A.apply(this, arguments);
this.street = "Downing Street 10";
}
inheritance(B, A);
function mixin() {
}
inheritance(mixin);
mixin.prototype.getProperties = function() {
return {
name: this.name,
address: this.street,
year: this.year
};
};
function C() {
B.apply(this, arguments);
this.year = "2018"
}
inheritance(C, B, mixin);
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
Note
Object.create can be safely used in every modern browser, including IE9+. Object.assign does not work in any version of IE nor some mobile browsers. It is recommended to polyfill Object.create and/or Object.assign if you want to use them and support browsers that do not implement them.
You can find a polyfill for Object.create here
and one for Object.assign here.
How about this simple approach
function Body(){
this.Eyes = 2;
this.Arms = 2;
this.Legs = 2;
this.Heart = 1;
this.Walk = function(){alert(this.FirstName + ' Is Walking')};
}
function BasePerson() {
var BaseBody = new Body(this);
BaseBody.FirstName = '';
BaseBody.LastName = '';
BaseBody.Email = '';
BaseBody.IntroduceSelf = function () { alert('Hello my name is ' + this.FirstName + ' ' + this.LastName); };
return BaseBody;
}
function Person(FirstName,LastName)
{
var PersonBuild = new BasePerson();
PersonBuild.FirstName = FirstName;
PersonBuild.LastName = LastName;
return PersonBuild;
}
var Person1 = new Person('Code', 'Master');
Person1.IntroduceSelf();
Person1.Walk();
//
// try this one:
//
// function ParentConstructor() {}
// function ChildConstructor() {}
//
// var
// SubClass = ChildConstructor.xtendz( ParentConstructor );
//
Function.prototype.xtendz = function ( SuperCtorFn ) {
return ( function( Super, _slice ) {
// 'freeze' host fn
var
baseFn = this,
SubClassCtorFn;
// define child ctor
SubClassCtorFn = function ( /* child_ctor_parameters..., parent_ctor_parameters[] */ ) {
// execute parent ctor fn on host object
// pass it last ( array ) argument as parameters
Super.apply( this, _slice.call( arguments, -1 )[0] );
// execute child ctor fn on host object
// pass remaining arguments as parameters
baseFn.apply( this, _slice.call( arguments, 0, -1 ) );
};
// establish proper prototype inheritance
// 'inherit' methods
SubClassCtorFn.prototype = new Super;
// (re)establish child ctor ( instead of Super ctor )
SubClassCtorFn.prototype.constructor = SubClassCtorFn;
// return built ctor
return SubClassCtorFn;
} ).call( this, SuperCtorFn, Array.prototype.slice );
};
// declare parent ctor
function Sup( x1, x2 ) {
this.parent_property_1 = x1;
this.parent_property_2 = x2;
}
// define some methods on parent
Sup.prototype.hello = function(){
alert(' ~ h e l l o t h e r e ~ ');
};
// declare child ctor
function Sub( x1, x2 ) {
this.child_property_1 = x1;
this.child_property_2 = x2;
}
var
SubClass = Sub.xtendz(Sup), // get 'child class' ctor
obj;
// reserve last array argument for parent ctor
obj = new SubClass( 97, 98, [99, 100] );
obj.hello();
console.log( obj );
console.log('obj instanceof SubClass -> ', obj instanceof SubClass );
console.log('obj.constructor === SubClass -> ', obj.constructor === SubClass );
console.log('obj instanceof Sup -> ', obj instanceof Sup );
console.log('obj instanceof Object -> ', obj instanceof Object );
//
// Object {parent_property_1: 99, parent_property_2: 100, child_property_1: 97, child_property_2: 98}
// obj instanceof SubClass -> true
// obj.constructor === SubClass -> true
// obj instanceof Sup -> true
// obj instanceof Object -> true
//
The easiest way to use AWeb library. Official sample:
/**
* A-class
*/
var ClassA = AWeb.class({
public : {
/**
* A-class constructor
*/
constructor : function() {
/* Private variable */
this.variable1 = "A";
this.calls = 0;
},
/**
* Function returns information about the object
*/
getInfo : function() {
this.incCalls();
return "name=" + this.variable1 + ", calls=" + this.calls;
}
},
private : {
/**
* Private function
*/
incCalls : function() {
this.calls++;
}
}
});
/**
* B-class
*/
var ClassB = AWeb.class({
extends : ClassA,
public : {
/**
* B-class constructor
*/
constructor : function() {
this.super();
/* Private variable */
this.variable1 = "B";
},
/**
* Function returns extended information about the object
*/
getLongInfo : function() {
return this.incCalls !== undefined ? "incCalls exists" : "incCalls undefined";
}
}
});
/**
* Main project function
*/
function main() {
var a = new ClassA(),
b = new ClassB();
alert(
"a.getInfo " + (a.getInfo ? "exists" : "undefined") + "\n" +
"a.getLongInfo " + (a.getLongInfo ? "exists" : "undefined") + "\n" +
"b.getInfo " + (b.getInfo ? "exists" : "undefined") + "\n" +
"b.getLongInfo " + (b.getLongInfo ? "exists" : "undefined") + "\n" +
"b.getInfo()=" + b.getInfo() + "\n" +
"b.getLongInfo()=" + b.getLongInfo()
);
}
I found a solution much easier than extend and prototyping things. Actually I don't know how efficient this is though it looks clean and functional.
var A = function (p) {
if (p == null) p = this;
p.a1 = 0;
this.a2 = 0;
var a3 = 0;
};
var B = function (p) {
if (p == null) p = this;
p.b1 = new A(this);
this.b2 = new A(this);
var b3 = new A(this);
this b4 = new A();
};
var a = new A ();
var b = new B ();
result:
a
a1 0
a2 0
b
a1 0
b1
a2 0
b2
a2 0
b4
a1 0
a2 0
practical example:
var Point = function (p) {
if (p == null) p = this;
var x = 0;
var y = 0;
p.getPoint = function () { return [x,y]; };
p.setPoint = function (_x,_y) { x = _x; y = _y; };
};
var Dimension = function (p) {
if (p == null) p = this;
var w = 0;
var h = 0;
p.getDimension = function() { return [w,h] };
p.setDimension = function(_w,_h) { w = _w; h = _h };
};
var Rect = function (p) {
if (p == null) p = this;
var dimension = new Dimension(this);
var location = new Point(this);
};
var rect = new Rect ();
rect.setDimension({w:30,h:40});
rect.setPoint({x:50,y:50});
is this a good pattern for OO JS?
What I am looking for is an easy way to solve inheritance in JavaScript.
function MySuperClass(arg)
{
this.arg1 = arg;
}
function MyBaseClass(arg)
{
this.base = MySuperClass;
this.base(arg);
this.arg2 = arg;
}
MyBaseClass.prototype = new MySuperClass();
function MySpecificClass(arg)
{
this.base = MyBaseClass;
this.base(arg);
this.arg3 = arg;
}
//ensures inheritance of all properties
MySpecificClass.prototype = new MyBaseClass();
var myFirstInstance = new MySpecificClass("test");
var mySecondInstance = new MySpecificClass("test2");
Note: See the end for an ES2015 update.
ES5 and earlier
There are a couple of problems there.
Your MySuperClass function expects an argument, but you can't give it one when you're calling it to create the MyBaseClass.prototype.
The base property you're setting on the instance won't work correctly for the code in MyBaseClass, because MyBaseClass expects that to be MySuperClass, but it isn't, because MySpecificClass has overwritten it.
This is complex stuff. You're very smart being sure to do three generations (MySuperClass, MyBaseClass, and MySpecificClass), because it's really easy to do this for just a two-level hierarchy, but for three+ levels, it's much more complicated. :-)
If you want a thorough discussion of dealing with inheritance, calling into superclass methods, etc., in JavaScript, I've written an article on it, and written a toolkit for doing it. Reading the article and looking at the toolkit source (which goes beyond the article) may be useful in understanding how the prototype chain works and how to work with it.
Here's an example not using any toolkit and not trying to make supercalls easy. To keep things clear, I've used the terms Parent, Child, and GrandChild for the three generations:
// A parent (base) "class"
function Parent(a) {
this.a = a;
}
Parent.prototype.one = function() {
console.log("I'm Parent#one: a = " + this.a);
};
Parent.prototype.two = function() {
console.log("I'm Parent#two: a = " + this.a);
};
// A child "subclass"
function Child(a, b) {
// Chain to "superclass" constructor
Parent.call(this, a);
// Do our own init
this.b = b;
}
// Create the prototype objct that `new Child` will assign to instances
// by creating a blank object backed by `Parent.prototype`. Also set
// the `constructor` property on the object; JavaScript defines that it
// will refer back to the function on the default prototype objects, so
// we do that for consistency despite nothing in JavaScript actually
// _using_ `constructor`.
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// Add things to `Child.prototype`
Child.prototype.one = function() {
Parent.prototype.one.call(this);
console.log("I'm Child#one: b = " + this.b);
};
Child.prototype.three = function() {
console.log("I'm Child#three: b = " + this.b);
};
// A grandchild "subclass"
function GrandChild(b, c) {
// Chain to "superclass" constructor
// Note that GrandChild has a fixed value for Parent's `a`
Child.call(this, "GrandChildFixedA", b);
// Do our own init
this.c = c;
}
// Again create a blank object to be the prototype `new GrandChild`
// assigns, again set `constructor`
GrandChild.prototype = Object.create(Child.prototype);
GrandChild.prototype.constructor = GrandChild;
// Add things to it
GrandChild.prototype.one = function() {
Child.prototype.one.call(this);
console.log("I'm GrandChild#one: c = " + this.c);
};
GrandChild.prototype.three = function() {
Child.prototype.three.call(this);
console.log("I'm GrandChild#three: c = " + this.c);
};
Usage:
var p = new Parent("ParentA");
console.log("Calling p.one");
p.one(); // "I'm Parent#one: a = ParentA"
console.log("Calling p.two");
p.two(); // "I'm Parent#two: a = ParentA"
var c = new Child("ChildA", "ChildB");
console.log("Calling c.one");
c.one(); // "I'm Parent#one: a = ChildA" then "I'm Child #one: b = ChildB"
console.log("Calling c.two");
c.two(); // "I'm Parent#two: a = ChildA"
console.log("Calling c.three");
c.three(); // "I'm Child#three: b = ChildB"
var gc = new GrandChild("GrandChildB", "GrandChildC");
console.log("Calling gc.one");
gc.one(); // "I'm Parent#one: a = GrandChildFixedA" then "I'm Child #one: b = GrandChildB" then "I'm GrandChild#one: c = GrandChildC"
console.log("Calling gc.two");
gc.two(); // "I'm Parent#two: a = GrandChildA"
console.log("Calling gc.three");
gc.three(); // "I'm Child#three: b = GrandChildB" then "I'm GrandChild#three: c = GrandChildC"
Testing instanceof, although if you're using instanceof a lot, you might want to read up on duck typing:
// Some things that should be true
console.log("p instanceof Parent? " + (p instanceof Parent));
console.log("c instanceof Parent? " + (c instanceof Parent));
console.log("c instanceof Child? " + (c instanceof Child));
console.log("gc instanceof Parent? " + (gc instanceof Parent));
console.log("gc instanceof Child? " + (gc instanceof Child));
console.log("gc instanceof GrandChild? " + (gc instanceof GrandChild));
// And some things that *shouldn't* be true:
console.log("p instanceof Child? (should be false) " + (p instanceof Child));
console.log("p instanceof GrandChild? (should be false) " + (p instanceof GrandChild));
console.log("c instanceof GrandChild? (should be false) " + (c instanceof GrandChild));
If you're not in an ES5-enabled environment, you can use this shim for Object.create (note: not a complete shim, just enough to enable the above):
Object.create = function(p) {
var o;
function ctor() {
}
ctor.prototype = p;
o = new ctor();
ctor.prototype = null;
return o;
};
You can see why a toolkit script makes life a bit easier. You have several to choose from. Here's what the above looks like using Lineage, my toolkit:
// A parent (base) "class"
var Parent = Lineage.define(function(p) {
p.initialize = function(a) {
this.a = a;
};
p.one = function() {
console.log("I'm Parent#one: a = " + this.a);
};
p.two = function() {
console.log("I'm Parent#two: a = " + this.a);
};
});
// A child "subclass"
var Child = Lineage.define(Parent, function(p, pp) {
p.initialize = function(a, b) {
// Chain to "superclass" constructor
pp.initialize.call(this, a);
// Do our own init
this.b = b;
};
p.one = function() {
pp.one.call(this);
console.log("I'm Child#one: b = " + this.b);
};
p.three = function() {
console.log("I'm Child#three: b = " + this.b);
};
});
// A grandchild "subclass"
var GrandChild = Lineage.define(Child, function(p, pp) {
p.initialize = function(b, c) {
// Chain to "superclass" constructor
// Note that GrandChild has a fixed value for Parent's `a`
pp.initialize.call(this, "GrandChildFixedA", b);
// Do our own init
this.c = c;
};
p.one = function() {
pp.one.call(this);
console.log("I'm GrandChild#one: c = " + this.c);
};
p.three = function() {
pp.three.call(this);
console.log("I'm GrandChild#three: c = " + this.c);
};
});
Usage is the same.
ES2015 and later
As of ES2015 (aka "ES6"), JavaScript got the class and super keywords, which dramatically simplify the above, and can be used today with transpiling.
class Parent {
constructor(a) {
this.a = a;
}
one() {
console.log("I'm Parent#one: a = " + this.a);
}
two() {
console.log("I'm Parent#two: a = " + this.a);
}
}
class Child extends Parent {
constructor(a) {
super(a);
}
one() {
super.one();
console.log("I'm Child#one: a = " + this.a);
}
three() {
console.log("I'm Child#three: a = " + this.a);
}
}
class GrandChild extends Child {
constructor(a) {
super(a);
}
one() {
super.one();
console.log("I'm GrandChild#one: a = " + this.a);
}
three() {
super.three();
console.log("I'm GrandChild#three: a = " + this.a);
}
}
// Usage
var p = new Parent("ParentA");
console.log("Calling p.one");
p.one(); // "I'm Parent#one: a = ParentA"
console.log("Calling p.two");
p.two(); // "I'm Parent#two: a = ParentA"
var c = new Child("ChildA", "ChildB");
console.log("Calling c.one");
c.one(); // "I'm Parent#one: a = ChildA" then "I'm Child #one: b = ChildB"
console.log("Calling c.two");
c.two(); // "I'm Parent#two: a = ChildA"
console.log("Calling c.three");
c.three(); // "I'm Child#three: b = ChildB"
var gc = new GrandChild("GrandChildB", "GrandChildC");
console.log("Calling gc.one");
gc.one(); // "I'm Parent#one: a = GrandChildFixedA" then "I'm Child #one: b = GrandChildB" then "I'm GrandChild#one: c = GrandChildC"
console.log("Calling gc.two");
gc.two(); // "I'm Parent#two: a = GrandChildA"
console.log("Calling gc.three");
gc.three(); // "I'm Child#three: b = GrandChildB" then "I'm GrandChild#three: c = GrandChildC"
I'm using this approach:
var func1 = function(parameter1, parameter2) {
// do your stuff here
}
var func2 = function(parameter1, parameter2, parameter3) {
// call the constructor of func1 with actual 'this'
func1.call(this, parameter1, parameter2);
// do your specific task here
}
func2.prototype = func1.prototype;
func2.prototype.constructor = func2;
works fine :)
In Douglas Crockford's JavaScript: The Good Parts he recommends that we use functional inheritance. Here's an example:
var mammal = function(spec, my) {
var that = {};
my = my || {};
// Protected
my.clearThroat = function() {
return "Ahem";
};
that.getName = function() {
return spec.name;
};
that.says = function() {
return my.clearThroat() + ' ' + spec.saying || '';
};
return that;
};
var cat = function(spec, my) {
var that = {};
my = my || {};
spec.saying = spec.saying || 'meow';
that = mammal(spec, my);
that.purr = function() {
return my.clearThroat() + " purr";
};
that.getName = function() {
return that.says() + ' ' + spec.name + ' ' + that.says();
};
return that;
};
var kitty = cat({name: "Fluffy"});
The main issue I have with this is that every time I make a mammal or cat the JavaScript interpreter has to re-compile all the functions in it. That is, you don't get to share the code between instances.
My question is: how do I make this code more efficient? For example, if I was making thousands of cat objects, what is the best way to modify this pattern to take advantage of the prototype object?
Well, you just can't do it that way if you plan on making lots of mammal or cat. Instead do it the old fashioned way (prototype) and inherit by property. You can still do the constructors the way you have above but instead of that and my you use the implicit this and some variable representing the base class (in this example, this.mammal).
cat.prototype.purr = function() { return this.mammal.clearThroat() + "purr"; }
I'd use another name than my for base access and store it in this in the cat constructor. In this example I used mammal but this might not be the best if you want to have static access to the global mammal object. Another option is to name the variable base.
Let me introduce you to Classical Inheritance that never uses prototype. This is a bad coding exercise but will teach you the real Classical Inheritance which always compared to prototypal inheritance:
Make a custructor:
function Person(name, age){
this.name = name;
this.age = age;
this.sayHello = function(){return "Hello! this is " + this.name;}
}
Make another cunstructor that inherits from it:
function Student(name, age, grade){
Person.apply(this, [name, age]);
this.grade = grade
}
Very simple! Student calls(applies) Person on itself with name and age arguments takes care of grade arguments by itself.
Now lets make an instance of Student.
var pete = new Student('Pete', 7, 1);
Out pete object will now contain name, age, grade and sayHello properties. It owns all those properties. They are not uplinked to Person through prototype. If we change Person to this:
function Person(name, age){
this.name = name;
this.age = age;
this.sayHello = function(){
return "Hello! this is " + this.name + ". I am " this.age + " years old";
}
}
pete will no recieve the update. If we call pete.sayHello, ti will return Hello! this is pete. It will not get the new update.
if you want privacy and you dont like protyping you may or may-not like this approach:
(note.: it uses jQuery.extend)
var namespace = namespace || {};
// virtual base class
namespace.base = function (sub, undefined) {
var base = { instance: this };
base.hierarchy = [];
base.fn = {
// check to see if base is of a certain class (must be delegated)
is: function (constr) {
return (this.hierarchy[this.hierarchy.length - 1] === constr);
},
// check to see if base extends a certain class (must be delegated)
inherits: function (constr) {
for (var i = 0; i < this.hierarchy.length; i++) {
if (this.hierarchy[i] == constr) return true;
}
return false;
},
// extend a base (must be delegated)
extend: function (sub) {
this.hierarchy.push(sub.instance.constructor);
return $.extend(true, this, sub);
},
// delegate a function to a certain context
delegate: function (context, fn) {
return function () { return fn.apply(context, arguments); }
},
// delegate a collection of functions to a certain context
delegates: function (context, obj) {
var delegates = {};
for (var fn in obj) {
delegates[fn] = base.fn.delegate(context, obj[fn]);
}
return delegates;
}
};
base.public = {
is: base.fn.is,
inherits: base.fn.inherits
};
// extend a sub-base
base.extend = base.fn.delegate(base, base.fn.extend);
return base.extend(sub);
};
namespace.MyClass = function (params) {
var base = { instance: this };
base.vars = {
myVar: "sometext"
}
base.fn = {
init: function () {
base.vars.myVar = params.myVar;
},
alertMyVar: function() {
alert(base.vars.myVar);
}
};
base.public = {
alertMyVar: base.fn.alertMyVar
};
base = namespace.base(base);
base.fn.init();
return base.fn.delegates(base,base.public);
};
newMyClass = new namespace.MyClass({myVar: 'some text to alert'});
newMyClass.alertMyVar();
the only downside is that because of the privacy scope you can only extend the virtual classes and not the instanceable classes.
here is an example of how i extend the namespace.base, to bind/unbind/fire custom events.
// virtual base class for controls
namespace.controls.base = function (sub) {
var base = { instance: this };
base.keys = {
unknown: 0,
backspace: 8,
tab: 9,
enter: 13,
esc: 27,
arrowUp: 38,
arrowDown: 40,
f5: 116
}
base.fn = {
// bind/unbind custom events. (has to be called via delegate)
listeners: {
// bind custom event
bind: function (type, fn) {
if (fn != undefined) {
if (this.listeners[type] == undefined) {
throw (this.type + ': event type \'' + type + '\' is not supported');
}
this.listeners[type].push(fn);
}
return this;
},
// unbind custom event
unbind: function (type) {
if (this.listeners[type] == undefined) {
throw (this.type + ': event type \'' + type + '\' is not supported');
}
this.listeners[type] = [];
return this;
},
// fire a custom event
fire: function (type, e) {
if (this.listeners[type] == undefined) {
throw (this.type + ': event type \'' + type + '\' does not exist');
}
for (var i = 0; i < this.listeners[type].length; i++) {
this.listeners[type][i](e);
}
if(e != undefined) e.stopPropagation();
}
}
};
base.public = {
bind: base.fn.listeners.bind,
unbind: base.fn.listeners.unbind
};
base = new namespace.base(base);
base.fire = base.fn.delegate(base, base.fn.listeners.fire);
return base.extend(sub);
};
To proper use Javascript-prototype based inheritance you could use fastClass https://github.com/dotnetwise/Javascript-FastClass
You have the simpler inheritWith flavor:
var Mammal = function (spec) {
this.spec = spec;
}.define({
clearThroat: function () { return "Ahem" },
getName: function () {
return this.spec.name;
},
says: function () {
return this.clearThroat() + ' ' + spec.saying || '';
}
});
var Cat = Mammal.inheritWith(function (base, baseCtor) {
return {
constructor: function(spec) {
spec = spec || {};
baseCtor.call(this, spec);
},
purr: function() {
return this.clearThroat() + " purr";
},
getName: function() {
return this.says() + ' ' + this.spec.name + this.says();
}
}
});
var kitty = new Cat({ name: "Fluffy" });
kitty.purr(); // Ahem purr
kitty.getName(); // Ahem Fluffy Ahem
And if you are very concerned about performance then you have the fastClass flavor:
var Mammal = function (spec) {
this.spec = spec;
}.define({
clearThroat: function () { return "Ahem" },
getName: function () {
return this.spec.name;
},
says: function () {
return this.clearThroat() + ' ' + spec.saying || '';
}
});
var Cat = Mammal.fastClass(function (base, baseCtor) {
return function() {
this.constructor = function(spec) {
spec = spec || {};
baseCtor.call(this, spec);
};
this.purr = function() {
return this.clearThroat() + " purr";
},
this.getName = function() {
return this.says() + ' ' + this.spec.name + this.says();
}
}
});
var kitty = new Cat({ name: "Fluffy" });
kitty.purr(); // Ahem purr
kitty.getName(); // Ahem Fluffy Ahem
Btw, your initial code doesn't make any sense but I have respected it literally.
fastClass utility:
Function.prototype.fastClass = function (creator) {
var baseClass = this, ctor = (creator || function () { this.constructor = function () { baseClass.apply(this, arguments); } })(this.prototype, this)
var derrivedProrotype = new ctor();
if (!derrivedProrotype.hasOwnProperty("constructor"))
derrivedProrotype.constructor = function () { baseClass.apply(this, arguments); }
derrivedProrotype.constructor.prototype = derrivedProrotype;
return derrivedProrotype.constructor;
};
inheritWith utility:
Function.prototype.inheritWith = function (creator, makeConstructorNotEnumerable) {
var baseCtor = this;
var creatorResult = creator.call(this, this.prototype, this) || {};
var Derrived = creatorResult.constructor ||
function defaultCtor() {
baseCtor.apply(this, arguments);
};
var derrivedPrototype;
function __() { };
__.prototype = this.prototype;
Derrived.prototype = derrivedPrototype = new __;
for (var p in creatorResult)
derrivedPrototype[p] = creatorResult[p];
if (makeConstructorNotEnumerable && canDefineNonEnumerableProperty) //this is not default as it carries over some performance overhead
Object.defineProperty(derrivedPrototype, 'constructor', {
enumerable: false,
value: Derrived
});
return Derrived;
};
define utility:
Function.prototype.define = function (prototype) {
var extendeePrototype = this.prototype;
if (prototype)
for (var p in prototype)
extendeePrototype[p] = prototype[p];
return this;
}
[* Disclaimer, I am the author of the open source package and the names of the methods themselves might be renamed in future` *]