I'm looking for a simple way of creating two classes, one inheriting from the other, and the child redefining one of the parent's methods, and inside the new method, calling the parent's.
For example, having a class Animal and Dog, where the Animal class defines a method makeSound() which establishes how to output a sound, which Dog then overrides in its own makeSound() method to make a "woof" sound, but while also calling Animal's makeSound() to output that woof.
I looked at John Resig's model here, but it uses the native arguments.callee property which is apparently depreciated in ECMA script 5. Does that mean I shouldn't use John Resig's code?
What would one neat, simple way of writing my animal/dog code using Javascript's prototype inheritance model?
Does that mean I shouldn't use John Resig's code?
Correct, not when you are using ES5 in strict mode. However, it can be easily adapted:
/* Simple JavaScript Inheritance for ES 5.1
* based on http://ejohn.org/blog/simple-javascript-inheritance/
* (inspired by base2 and Prototype)
* MIT Licensed.
*/
(function(global) {
"use strict";
var fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
function BaseClass(){}
// Create a new Class that inherits from this class
BaseClass.extend = function(props) {
var _super = this.prototype;
// Set up the prototype to inherit from the base class
// (but without running the init constructor)
var proto = Object.create(_super);
// Copy the properties over onto the new prototype
for (var name in props) {
// Check if we're overwriting an existing function
proto[name] = typeof props[name] === "function" &&
typeof _super[name] == "function" && fnTest.test(props[name])
? (function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, props[name])
: props[name];
}
// The new constructor
var newClass = typeof proto.init === "function"
? proto.hasOwnProperty("init")
? proto.init // All construction is actually done in the init method
: function SubClass(){ _super.init.apply(this, arguments); }
: function EmptyClass(){};
// Populate our constructed prototype object
newClass.prototype = proto;
// Enforce the constructor to be what we expect
proto.constructor = newClass;
// And make this class extendable
newClass.extend = BaseClass.extend;
return newClass;
};
// export
global.Class = BaseClass;
})(this);
Prototype chain with Object.create() + assign constructor
function Shape () {
this.x = 0;
this.y = 0;
}
Shape.prototype.move = function (x, y) {
this.x += x;
this.y += y;
};
function Rectangle () {
Shape.apply(this, arguments); // super constructor w/ Rectangle configs if any
}
Rectangle.prototype = Object.create(Shape.prototype); // inherit Shape functionality
// works like Rectangle.prototype = new Shape() but WITHOUT invoking the constructor
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
rect instanceof Rectangle && rect instanceof Shape // returns true
from Object.create documentation
information about the new keyword
This is something I came up with for inheritance using chaining as well as allowing _super to work.
/**
* JavaScript simple inheritance
* by Alejandro Gonzalez Sole (base on John Resig's simple inheritance script)
* MIT Licensed.
**/
(function (){
var initializing = false,
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.* /;
function Class(){};
function inheritClass(superClass){
var self = this;
function Class(){
if (!initializing && typeof this._constructor === 'function')
this._constructor.apply(this, arguments);
}
Class.prototype = superClass.prototype;
Class.prototype._constructor = superClass;
Class.prototype.constructor = Class;
Class.extend = extendClass;
//currenlty if you inhert multiple classes it breaks
Class.inherit = inheritClass;
return Class;
};
function extendClass(prop) {
var self = this;
var _super = self.prototype;
function Class(){
if (!initializing && typeof this._constructor === 'function')
this._constructor.apply(this, arguments);
}
initializing = true;
var prototype = new self();
initializing = false;
for (var name in prop) {
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) : prop[name];
}
Class.prototype = prototype;
Class.prototype.constructor = Class;
Class.extend = extendClass;
Class.inherit = inheritClass;
return Class;
};
Class.extend = extendClass;
Class.inherit = inheritClass;
})();
//EXAMPLE
function Person(){
this.name = "No name";
console.log("PERSON CLASS CONSTRUCTOR")
}
Person.prototype.myMethod = function (t){
console.log("MY PERSON", t, this.name);
return -1;
}
var TestPerson = Class.inherit(Person).extend({
_constructor: function(){
this._super();
this.name = "JOhn";
console.log("TEST PERSON CONSTRUCTOR");
},
myMethod: function (t){
console.log("TEST PERSON", t, this.name);
return this._super(t)
}
});
var test = new TestPerson();
console.log(test.myMethod("BA"));
I've been testing it on my pixi wrapper https://github.com/guatedude2/pixijs-cli so far it's been working very well for me.
The only issue i've encountered with this approach is that you can only inherit once. If you run inherit again it will override the previous inheritance.
I prefer the way TypeScript generates a form of inheritance (Select Simple Inheritance from the dropdown). That one doesn't use arguments.callee, but an __extends prototype.
var __extends = this.__extends || function (d, b) {
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var Animal = (function () {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function (meters) {
alert(this.name + " moved " + meters + "m.");
};
return Animal;
})();
var Snake = (function (_super) {
__extends(Snake, _super);
function Snake(name) {
_super.call(this, name);
}
Snake.prototype.move = function () {
alert("Slithering...");
_super.prototype.move.call(this, 5);
};
return Snake;
})(Animal);
var Horse = (function (_super) {
__extends(Horse, _super);
function Horse(name) {
_super.call(this, name);
}
Horse.prototype.move = function () {
alert("Galloping...");
_super.prototype.move.call(this, 45);
};
return Horse;
})(Animal);
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
Related
I want to create a subclass from a superclass using prototypes in javascript, so I set them up:
var SuperClass = function() {
this.a = 1;
this.get = function() {return this.a;}
this.init = function() {
CreateStrangeThings();
}
this.init();
}
var SubClass = function() {
this.b = 2;
this.get = function() {return this.b;}
}
SubClass.prototype = new SuperClass();
The problem is, the line SubClass.prototype = new SuperClass(); calls the CreateStrangeThings(); function. I want this function to be called only when I create the subclass object (var subOjbect = new SubClass();)
How to overcome this issue? Maybe I should use SubClass.prototype = SuperClass; ? Is it wise?
EDIT:
SubClass.prototype = SuperClass;
removes "a" property from SubClass
The problem is, the line SubClass.prototype = new SuperClass(); calls the CreateStrangeThings(); function.
Yes. That's why that pattern, despite being often-cited, is wrong. Another reason is that it ignores the possibility the superclass constructor may require arguments.
Instead, create the prototype object without calling the super constructor:
SubClass.prototype = Object.create(SuperClass.prototype);
SubClass.prototype.constructor = SubClass;
...and also call the super constructor inside SubClass:
var SubClass = function() {
SuperClass.call(this);
this.b = 2;
this.get = function() {return this.b;}
};
So all together we get:
var SuperClass = function() {
this.a = 1;
this.get = function() {return this.a;}
this.init = function() {
CreateStrangeThings();
}
this.init();
};
var SubClass = function() {
SuperClass.call(this);
this.b = 2;
this.get = function() {return this.b;}
};
SubClass.prototype = Object.create(SuperClass.prototype);
SubClass.prototype.constructor = SubClass;
Or of course, use ES2015 and a transpiler.
class SuperClass {
constructor() {
this.a = 1;
this.get = function() {return this.a;};
this.init = function() {
CreateStrangeThings();
};
this.init();
}
}
class SubClass {
constructor() {
super();
this.b = 2;
this.get = function() {return this.b;};
}
}
How to overcome this issue? Maybe I should use SubClass.prototype = SuperClass; ? Is it wise?
No. If you want objects created via new SubClass to inherit properties from SuperClass.prototype, you need SubClass.prototype to use SuperClass.prototype as its prototype. SuperClass doesn't; the prototype of SuperClass is Function.prototype, not SubClass.prototype. (Ignore the name of the prototype property on functions; it isn't their prototype, it's the prototype new assigns to objects created via new TheFunction.)
I have this function: (which is I guess abstract factory for creating javascript objects)
var $class = function(definition) {
var constructor = definition.constructor;
var parent = definition.Extends;
if (parent) {
var F = function() { };
constructor._superClass = F.prototype = parent.prototype;
constructor.prototype = new F();
}
for (var key in definition) {
constructor.prototype[key] = definition[key];
}
constructor.prototype.constructor = constructor;
return constructor;
};
A use it for defining classes C/java syntax like with polymorphism and extending:
var Bullet = $class({
Extends: GameObject,
constructor: function(texturePath, x, y, ctx, direction, passable, player) {
GameObject.call(this, texturePath, x, y, ctx, 1, 1, passable, new Array(8, 8, 0, 0));
},
isActive: function() {
},
getPlayer: function() {
},
update: function(dt) {
},
destroy: function() {
},
processCollision: function() {
}
});
And then calling:
var bullet = new Bullet(params);
I tried to rewrite it into nodejs module like this:
(function(){
var $class = function(definition) {
var constructor = definition.constructor;
var parent = definition.Extends;
if (parent) {
var F = function() { };
constructor._superClass = F.prototype = parent.prototype;
constructor.prototype = new F();
}
for (var key in definition) {
constructor.prototype[key] = definition[key];
}
constructor.prototype.constructor = constructor;
return constructor;
};
module.exports.createClass = function() {
return $class();
}
});
And then call it with:
var c = require(__dirname + "\\Class");
var Bullet = c.createClass({
Extends: GameObject,
constructor: function() {}
});
But it doesn't work, can you please help me with rewriting?
UPDATE: I rewrited it from #Salem answer, but I lost extending and polymorphism in process.
In order to have extending I simply have to write instead of
Extends: ParentClass
this:
Extends: ParentClass.constructor
I've expected that polymorphism would be something like:
// in class which is extended from ParentClass
ParentClass.method();
// in parent class adding line
module.exports.method = ParentClass.method;
But this is undefined. So where is the catch?
FINALLY I used mochiscript module for nodejs, it is even better syntax sugar with more object oriented functionality.
In your code, createClass is a function without any parameter. Also you call $class without any paramter also.
You don't need to wrap all your code in a function, because everything you declare there won't be accessible from outside unless you export it. So it should be something like this:
var func = function(definition) {
var constructor = definition.constructor;
var parent = definition.Extends;
if (parent) {
var F = function() { };
constructor._superClass = F.prototype = parent.prototype;
constructor.prototype = new F();
}
for (var key in definition) {
constructor.prototype[key] = definition[key];
}
constructor.prototype.constructor = constructor;
return constructor;
};
module.exports.createClass = func;
This means that if you require this module as X, the only thing you can access is X.createClass, and not X.func or anything else.
Is there a small, lightweight solution for javascript class inheritance that will work well on both client and server side (node.js)? I'm not wanting a big library, just something that will allow me to declare a constructor and some methods, then have the ability for a class to inherit that.
John Resig outlines a simple inheritance framework in about 25 lines of code here. I have seen it used to good effect.
You can use it like this:
var Vehicle = Class.extend({
init: function(wheels) {
this.wheels = wheels;
}
});
var Truck = Vehicle.extend({
init: function(hp, wheels) {
this.horsepower = hp;
this._super(wheels);
},
printInfo: function() {
console.log('I am a truck and I have ' + this.horsepower + ' hp.');
}
});
var t = new Truck(4, 350);
t.printInfo();
take a look at https://github.com/ded/klass
I created this small library to use an ExtJs Style ClassManager. It's quite simple yet, but very flexible.
Install via node.js
npm install esf-core
Sample
Esf.define('A', {
a: null,
constructor: function (a) {
// Save var
this.a = a;
// Heyho
console.log('A');
},
foo: function (b) {
console.log('foo - ' + b);
}
});
Esf.define('B', {
b: null,
constructor: function (a, b) {
// Call super constructor
this.callParent(a);
// Save var
this.b = b;
// Heyho
console.log('B');
},
foo: function () {
this.callParent('bar');
}
}, {
extend: 'A'
});
// Use
var b = new B(1, 2);
// or
var b = Esf.create('B', 1, 2);
/*
* Output:
* A
* B
* foo - bar
*/
b.foo();
Repository
https://bitbucket.org/tehrengruber/esf-js-core
I've seen the prototype library used successfully.
I think this is much better than the init hax in the simple inheritance fw:
(function() {
var core = {
require : function(source) {
if ( typeof (source) != "object" || !source)
throw new TypeError("Object needed as source.");
for (var property in source)
if (source.hasOwnProperty(property) && !this.prototype.hasOwnProperty(property))
this.prototype[property] = source[property];
},
override : function(source) {
if ( typeof (source) != "object" || !source)
throw new TypeError("Object needed as source.");
for (var property in source)
if (source.hasOwnProperty(property))
this.prototype[property] = source[property];
},
extend : function(source) {
var superClass = this;
var newClass = source.hasOwnProperty("constructor") ? source.constructor : function() {
superClass.apply(this, arguments);
};
newClass.superClass = superClass;
var superClone = function() {
};
superClone.prototype = superClass.prototype;
newClass.prototype = new superClone();
newClass.prototype.constructor = newClass;
if (source)
newClass.override(source);
return newClass;
}
};
core.require.call(Function, core);
Function.create = function (source){
var newClass = source.hasOwnProperty("constructor") ? source.constructor : function() {};
newClass.override(source);
return newClass;
};
})();
The vehicle example with this:
var Vehicle = Function.create({
constructor : function(wheels) {
this.wheels = wheels;
}
});
var Truck = Vehicle.extend({
constructor : function(hp, wheels) {
this.horsepower = hp;
Vehicle.call(this, wheels);
},
printInfo : function() {
console.log('I am a truck and I have ' + this.horsepower + ' hp.');
}
});
var t = new Truck(4, 350);
t.printInfo();
I created a very lightweight library that works in-browser and in node.js. Its a super easy-to-use, bloatless library:
https://github.com/fresheneesz/proto
Example:
var Person = proto(function() { // prototype builder
this.init = function(legs, arms) { // constructor
this.legs = legs
this.arms = arms
}
this.getCaughtInBearTrap = function() { // instance method
this.legs -= 1
}
this.limbs = function() {
return this.arms + this.legs
}
})
var Girl = proto(Person, function() { // inheritance
this.haveBaby = function() {
return Person(2,2)
}
})
var g = Girl(2,2) // instantiation
g.getCaughtInBearTrap()
console.log("Girl has "+g.limbs()+" limbs")
console.log(": (")
var super_class = function(p) {
this.p = p + 1;
this.method = function(a, b) {
// some code
};
};
var base_class = function(o) {
this.o = o;
super_class.call(this, o);
this.method = function(a, b) {
// call super_class .method();
// some code
}
}
base_class.prototype = new super_class();
var bc = new base_class(0);
var v1 = bc.o; // 0
var v2 = bc.p; // 1
How can I call the super_class method when the name and properties are supposed to be identical. If I changed the name, I would just call this.method(3, 4); from within another function. I'm creating an extension class to another extension class, so changing the name of the function will not help me.
Also, storing the function in a private variable var pmethod = this.method; is sloppy at best.
Your current implementation has an error at super_class(this, o);. Either replace it with super_class.call(this, o), or correctly implement an initializer method:
// Basic super class method.
var Super_class = function(p) {
this.init(p); // Call initializer
};
// Define prototype properties and methods
Super_class.prototype = {
constructor: Super_class,
init: function(p) { this.p = p + 1; },
method: function(a, b) {
console.log("Super class method, arguments: " + [a,b]);
}
};
// Define base_class
var Base_class = function(o) {
this.o = o; // Initialize `o` property
this.init(o); // Initialize p variable through the initializer
};
// Extend `Base_class` prototype with `method`.
Base_class.prototype.method = function(a, b) {
// Call the method from the parent = Super_class.prototype.method
this.constructor.prototype.method(a, b);
};
Base_class.prototype = new Super_class; // Set prototype to `super_class`.
var bc = new Base_class(0);
var v1 = bc.o; // 0
var v2 = bc.p; // 1
bc.method('Hi: ', [v1, v2]); // Prints "Super class method, arguments: Hi [0,1]"
Alternatively, you can also push all methods of Base_class in Base_class itself and/or create a reference to the parent class:
// Define base_class
var Base_class = function(o) {
var __super__ = this.constructor.prototype;
this.o = o; // Initialize `o` property
this.init(o); // Initialize p variable through the initializer
Base_class.prototype.method = function(a, b) {
// Call the method from the parent = Super_class.prototype.method
__super__.method(a, b);
};
};
var super_class = function(p) {
this.p = p + 1;
this.method = function(a, b) {
// some code
};
};
var base_class = function(o) {
this.o = o;
super_class(o); // remove "this"
this.method = function(a, b) {
// call base.method();
// some code
}
}
base_class.prototype = new super_class();
base_class.prototype.constructor = base_class; //important: pointing the constructor back to the base class.
This is what the basic inheritance is done in JavaScript. If you want to get something fancy , use
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
See http://javascript.crockford.com/prototypal.html for more information.
I use Resig's makeClass() approach for constructors:
// makeClass - By John Resig (MIT Licensed)
// Allows either new User() or User() to be employed for construction.
function makeClass(){
return function(args){
if ( this instanceof arguments.callee ) {
if ( typeof this.init == "function" )
this.init.apply( this, (args && args.callee) ? args : arguments );
} else
return new arguments.callee( arguments );
};
}
// usage:
// ------
// class implementer:
// var MyType = makeClass();
// MyType.prototype.init = function(a,b,c) {/* ... */};
// ------
// class user:
// var instance = new MyType("cats", 17, "September");
// -or-
// var instance = MyType("cats", 17, "September");
//
var MyType = makeClass();
MyType.prototype.init = function(a,b,c) {
say("MyType init: hello");
};
MyType.prototype.Method1 = function() {
say("MyType.Method1: hello");
};
MyType.prototype.Subtype1 = makeClass();
MyType.prototype.Subtype1.prototype.init = function(name) {
say("MyType.Subtype1.init: (" + name + ")");
}
In that code, MyType() is a toplevel type, and MyType.Subtype1 is a nested type.
To use it, I can do:
var x = new MyType();
x.Method1();
var y = new x.Subtype1("y");
Can I get a reference to the instance of the parent type, within the init() for Subtype1() ?
How?
Nope, not unless you write a class implementation that tracks this "outer" class explicitly, Javascript won't be able to give this to you.
For example:
function Class(def) {
var rv = function(args) {
for(var key in def) {
if(typeof def[key] == "function" && typeof def[key].__isClassDefinition == "boolean")
def[key].prototype.outer = this;
this[key] = def[key];
}
if(typeof this.init == "function")
this.init.apply( this, (args && args.callee) ? args : arguments );
};
rv.prototype.outer = null;
rv.__isClassDefinition = true;
return rv;
}
var MyType = new Class({
init: function(a) {
say("MyType init: " + a);
say(this.outer);
},
Method1: function() {
say("MyType.Method1");
},
Subtype1: new Class({
init: function(b) {
say("Subtype1: " + b);
},
Method1: function() {
say("Subtype1.Method1");
this.outer.Method1();
}
})
});
var m = new MyType("test");
m.Method1();
var sub = new m.Subtype1("cheese");
sub.Method1();