Javascript Privilege Function Inheritance - javascript

I have spent the past few weeks doing a ton of research on Javascript inheritance. Firstly, I want to say that I am NOT trying to implement class-based inheritance in Javascript, however, I am trying to mimic inheritance CORRECTLY using Javascript's prototypal nature. This may be beating a dead horse, but here goes my question:
I have two simple functions:
function Animal(arg) {
var a = arg;
//privileged function has access to private variable
this.sayHi = function(name) {
// can access a here if needed
console.log('Hi: ' + name);
}
}
and:
function Person(arg) {
this.sayHi = function(name) {
super.sayHi(name + '!!!');
};
}
Person.inheritsFrom(Animal);
The following would be the expected result:
var animal = new Animal('cow');
animal.sayHi('John'); // Hi: John
var person = new Person('adult');
person.sayHi('Fred'); // Hi: Fred!!!
The "inheritsFrom()" method that I'm using for inheritance looks like:
Function.prototype.inheritsFrom = function(parent) {
if(parent.constructor == Function) { //Normal Inheritance
this.prototype = new parent;
this.prototype.constructor = this;
this.prototype.parent = parent.prototype;
} else { //Pure Virtual Inheritance
this.prototype = parent;
this.prototype.constructor = this;
this.prototype.parent = parent;
}
return this;
}
These are some of my references:
http://phrogz.net/JS/classes/OOPinJS2.html
http://www.crockford.com/javascript/inheritance.html
https://medium.com/javascript-scene/common-misconceptions-about-inheritance-in-javascript-d5d9bab29b0a
Lots of good info, still can't figure out how to inherit "privileged" methods as shown above. I'm able to call the privileged method of the parent class. Please see the following JS Fiddle: https://jsfiddle.net/jxjk5hm9/
Any guidance on inheriting privileged methods is greatly appreciated, thanks!

There is no pretty solution to this. First off,
this.prototype = new parent;
is not a good way to establish inheritance. Rather use
this.prototype = Object.create(parent.prototype);
See Benefits of using `Object.create` for inheritance for more info.
Inside the child constructor, you'd have to call the parent constructor, applying it to the current instance:
function Person(arg) {
Animal.call(this, arg);
}
then you have to iterate over all methods that have been attached to this by Animal and keep a reference to them:
function Person(arg) {
Animal.call(this, arg);
var privilegedSuper = Object.keys(this)
.filter(function(prop) { return typeof this[prop] === 'function'; }.bind(this))
.reduce(function(obj, prop) { return (obj[prop] = this[prop]), obj; }.bind(this));
}
which you can then reference in the overridden methods:
function Person(arg) {
Animal.call(this, arg);
var privilegedSuper = Object.keys(this)
.filter(function(prop) { return typeof this[prop] === 'function'; }.bind(this))
.reduce(function(obj, prop) { return (obj[prop] = this[prop]), obj; }.bind(this));
this.sayHi = function(name) {
privilegedSuper.sayHi.call(this, name + '!!!');
};
}
Is it worth the effort and the complexity? I don't think so.
Function.prototype.inheritsFrom = function(parent) {
if (parent.constructor == Function) { //Normal Inheritance
this.prototype = Object.create(parent.prototype);
this.prototype.constructor = this;
this.prototype.parent = parent.prototype;
} else { //Pure Virtual Inheritance
this.prototype = parent;
this.prototype.constructor = this;
this.prototype.parent = parent;
}
return this;
}
function Animal(arg) {
var a = arg;
//privileged function has access to private variable
this.sayHi = function(name) {
// can access a here if needed
console.log('Hi: ' + name + ' (and here is a: ' + a + ')');
}
}
function Person(arg) {
Animal.call(this, arg);
var privilegedSuper = Object.keys(this)
.filter(function(prop) {
return typeof this[prop] === 'function';
}.bind(this))
.reduce(function(obj, prop) {
return (obj[prop] = this[prop]), obj;
}.bind(this), {});
this.sayHi = function(name) {
privilegedSuper.sayHi.call(this, name + '!!!');
};
}
Person.inheritsFrom(Animal);
var animal = new Animal('cow');
animal.sayHi('John'); // Hi: John
var person = new Person('adult');
person.sayHi('Fred'); // Hi: Fred!!!

Related

Why should I use 'underbar(_)' for getting my function done?

I was studying inheritance of javascript and made a function to test it.
Here is the code:
function subClass(obj) {
const parent = (this === window) ? Function : this;
const F = function() {};
const child = function() {
const _parent = child.parent;
if(_parent && _parent !== Function) {
_parent.apply(this, arguments);
}
if(child.prototype._init) {
child.prototype._init.apply(this, arguments);
}
};
F.prototype = parent.prototype;
child.prototype = new F();
child.prototype.constructor = child;
child.parent = parent;
child.subClass = arguments.callee;
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
child.prototype[i] = obj[i];
}
}
return child;
}
Then I made a sample object to adapt the function subClass. :
const person_obj = {
_init: function() {
console.log("person init");
},
getName: function() {
return this.name;
},
setName: function(name) {
this.name = name;
}
};
Finally, here is the output code. :
const Person = subClass(person_obj);
const person = new Person();
person.setName("dorae");
console.log(person.getName());
Here is the question.
What I want as a result for console.log(person.getName()); is dorae,
but instead empty space comes out as a result.
However, when I change two 'this.name's in person_obj to 'this._name',
the exact result 'dorae' comes out.
Even though I came up to the result what I wanted,
I couldn't get the reason why I got the result.
I would appreciate if you let me know the reason why I get the answer only
when I use the underbar in front of the variable.
Thank you.

The equivalent to using __proto__?

I'm trying to use the revealing module pattern with inheritance. I seem to have it working fine, but it uses "__proto__", which I understand it's considered deprecated. Is there a better was to create the inheritance with out using "__proto__"?
var Person = (function() {
var _name;
var api = {
init: init,
getName: getName
}
return api;
function init(name) {
_name = name;
}
function getName() {
return _name;
}
}())
var Teacher = (function() {
var _subject = "Math";
var api = {
getSubject: getSubject,
say: say
}
api.__proto__ = Person;
return api;
function getSubject() {
return _subject;
}
function say() {
console.log("I am " + this.getName() + " and I teach " + _subject)
}
}());
Teacher.init("Bob");
Teacher.say() // I am Bob and I teach math
https://plnkr.co/edit/XbGx38oCyvRn79xnn2FR?p=preview
The direct equivalent – setting the prototype, still a bad idea – is Object.setPrototypeOf:
Object.setPrototypeOf(api, Person);
The normal way of creating an object based on a prototype with Object.create and then adding properties to it works fine here, though:
var api = Object.create(Person);
api.getSubject = getSubject;
api.say = say;
but ideally you would just use constructors:
class Person {
constructor(name) {
this._name = name;
}
getName() {
return this._name;
}
}
class Teacher extends Person {
constructor(name) {
super(name);
this._subject = 'Math';
}
getSubject() {
return this._subject;
}
say() {
console.log(`I am ${this.getName()} and I teach ${this.getSubject()}`);
}
}
var teacher = new Teacher('Bob');
teacher.say() // I am Bob and I teach math
without ES6:
function Person(name) {
this._name = name;
}
Person.prototype.getName = function () {
return this._name;
};
function Teacher(name) {
Person.call(this, name);
this._subject = 'Math';
}
Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.getSubject = function () {
return this._subject;
};
Teacher.prototype.say = function () {
console.log('I am ' + this.getName() + ' and I teach ' + this.getSubject());
};
var teacher = new Teacher('Bob');
teacher.say(); // I am Bob and I teach math

javascript prototype inheritance using constructor [duplicate]

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});

Inheritance in Prototype JS [duplicate]

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});

Javascript functional inheritance with prototypes

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` *]

Categories