I am always struggling with the JavaScript object/class inheritance thing. I also don't like the duplicate code in all the examples I can find (the name of the object needs to be written a few times).
As far as I understand, proper inheritance in JavaScript looks like this:
function Parent(v) {
console.log('Parent', v);
}
Parent.prototype.helloParent = function() {
console.log('hello parent');
}
function Child(v) {
Parent.call( this, 'from child');
console.log('Child');
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.helloChild = function() {
console.log('hello child');
}
c = new Child();
console.log(c instanceof Child);
c.helloParent();
c.helloChild();
In this example, to extend the "Parent" object, I have to write "Child" four times, "Parent" two times. I want to type them both just once once – because of DRY.
I also don't want to define a custom function for this inheritance stuff. That just feels odd for me, to need a user function for such a fundamental functionality (and it is getting hard to read unknown code, because you never know what this specific inheritance function is doing exactly).
So I tried to find a simpler version. However I am not sure if I missed something?
function Parent(v) {
console.log('Parent', v);
this.helloParent = function() {
console.log('hello parent');
}
}
(Child = function(v) {
this.constructor('from child');
console.log('Child');
this.helloChild = function() {
console.log('hello child');
}
}).prototype = Parent.prototype;
c = new Child();
console.log(c instanceof Child);
c.helloParent();
c.helloChild();
Is this okay or does it have serious drawbacks?
Edit: Regarding the comments, sadly it seems that it has some serious drawback. Are there any other solutions to reduce at least to write the name of the parent object multiple times?
I use two very small functions for simplifying inheritance in JavaScript:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, keys) {
var prototype = Object.create(constructor.prototype);
for (var key in keys) prototype[key] = keys[key];
return defclass(prototype);
}
It is used as follows:
var Parent = defclass({
constructor: function (a) {
console.log("Parent", a);
},
helloParent: function () {
console.log("helloParent");
}
});
var Child = extend(Parent, {
constructor: function () {
Parent.call(this, "fromChild");
console.log("Child");
},
helloChild: function () {
console.log("helloChild");
}
});
Finally:
var child = new Child;
console.log(child instanceof Child);
child.helloParent();
child.helloChild();
Putting it all together:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, keys) {
var prototype = Object.create(constructor.prototype);
for (var key in keys) prototype[key] = keys[key];
return defclass(prototype);
}
var Parent = defclass({
constructor: function (a) {
console.log("Parent", a);
},
helloParent: function () {
console.log("helloParent");
}
});
var Child = extend(Parent, {
constructor: function () {
Parent.call(this, "fromChild");
console.log("Child");
},
helloChild: function () {
console.log("helloChild");
}
});
var child = new Child;
console.log(child instanceof Child);
child.helloParent();
child.helloChild();
Hope that helps.
oop in javascript is ugly. (at least until ES6 which supports the class and implements keywords) but even ES6 will not support multiple inheritance. I wrote a small class library (available on github) for javascript that makes creating classes and inheritance much easier to both develop and maintain. for example to create a class just do this:
ds.make.class({
type: 'a',
constructor: function (x) { this.val = x; },
mul: function (s) {
this.val *= s;
return this;
}
});
// now to inherit class a just do this...
ds.make.class({
type: 'b',
inherits: a,
constructor: function (x) { this.val = x; },
sub: function (s) {
this.val -= s;
return this;
}
});
var o = new b(5);
var output = o.mul(3).sub(5).val; // output = 10
Related
I'm not sure on the best approach to have object properties that are individual for each object in a OLOO inheritance chain.
Check this fiddle or consider the following code:
http://jsfiddle.net/HB7LU/19413/
Parent = {
array: [],
add: function(element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function() {
return this.array;
}
};
Child = Object.create(Parent, {
removeAllButOne: { value: function() {
this.array.splice(1);
return this;
}}
});
foo = Object.create(Parent);
foo.add('foo');
bar = Object.create(Child);
bar.add('bar');
In the fiddle a click on the foo or bar text will call the foo.add(...) or bar.add(...) function to add an element to the objects array, resulting in one extra <p> tag in the output.
The result is not what I want. Both foo and bar share the same array. But its easy to understand what happens, if we look up the object inheritance we can see the following:
Ok then, what can I do go get around this? There were two options that came to my mind:
Option 1)
http://jsfiddle.net/HB7LU/19419/
Parent = function() {
return {
array: [],
add: function(element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function() {
return this.array;
}
};
};
Child = Object.create(Parent(), {
removeAllButOne: { value: function() {
this.array.splice(1);
return this;
}}
});
foo = Object.create(Parent());
foo.add('foo');
bar = Object.create(Child);
bar.add('bar');
This would create a new Parent object, creating all the functions of the Parent object each time a Parent object is created or a child "inherits" from a (new) Parent object. While this solves the problem I had, it seems like a bad idea to always recreate the same functions over and over again for each child type object.
Option 2)
http://jsfiddle.net/HB7LU/19420/
Parent = Object.create({
add: function(element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function() {
return this.array;
}
}, {
ctor: { value: function(someArgs) {
this.array = [];
// maybe use someArgs
return this;
}}
});
Child = Object.create(Parent, {
removeAllButOne: { value: function() {
this.array.splice(1);
return this;
}}
});
foo = Object.create(Parent).ctor();
foo.add('foo');
bar = Object.create(Child).ctor();
bar.add('bar');
This seems to also solve the problem but avoids the recreation of the Parent object and its functions. So is this the way to go? What if I had multiple children in the inheritance chain that also have private properties?
Something like this?
Child = Object.create(Parent, {
ctor: { value: function(someArgs) {
this.__proto__.ctor(someArgs);
this.otherPrivate = {};
// maybe use someArgs
return this;
}},
removeAllButOne: { value: function() {
this.array.splice(1);
return this;
}}
});
Children would be shadowing the parent ctor with their own function... but in their ctor function they could call the parents ctor to not break functionality.
Thoughts and advice is highly appreciated, thanks!
Easiest way is to use Constructors so array is always created as an own property on the instance
// define Parent
function Parent() {
this.array = []; // array will be an instance property
}
Parent.prototype = {}; // inherit all the goodies from Object.prototype
Object.assign(Parent.prototype, { // using `Object.assign` for shorthand
add: function (element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function () {
return this.array;
}
});
// define Child
function Child() {
Parent.apply(this); // apply Parent constructor to the instance
}
Child.prototype = Object.create(Parent.prototype); // inherit Parent's prototype chain
Object.assign(Child.prototype, {
removeAllButOne: function () {
this.array.splice(1);
return this;
}
});
Now have
var a = new Child(),
b = new Child();
a.array === b.array; // false
You could also write this using ES 6's classes, but that is just syntactic sugar for what I've written above and will result in the same structures.
OLOO favours composition over inheritance. You could use a factory method pattern with Object.assign to compose objects with simple prototype delegation:
// Composable prototype objects, or "traits"
var base = {
add: function(element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function() {
return this.array;
}
};
var canRemoveAllButOne = {
removeAllButOne: function() {
this.array.splice(1);
return this;
}
}
// Factory functions
// You could think of these like external constructors
function createBase() {
return Object.assign({}, base, {
array: []
})
}
function createComposed() {
var base = createBase();
return Object.assign(base, canRemoveAllButOne)
}
// Test
function log(s) {
document.write(s + "<br>");
}
var b1 = createBase();
var b2 = createBase();
var c1 = createComposed();
var c2 = createComposed();
b1.add(1);
b1.add(2);
b2.add(9);
c1.add('a');
c2.add('b');
log(b1.getAll());
log(b2.getAll());
log(c1.getAll());
log(c2.getAll());
I have been using the John Resig javascript class implementation in my web apps, but the tests shows it is really slow. I really find it useful for the way of extending objects, and the benefits got from having a better code and less redundancy.
In some post was explained that it was slow because of the how the _super method is handled.
Since super is Java style, and most of time I develop in PHP, I made my own version of Resig implementation using the parent:: style (used in PHP), with the aim to make this faster. Here it is:
(function () {
this.Class = function () {
};
Class.extend = function extend(prop) {
var prototype = new this();
prototype.parent = this.prototype;
for (var name in prop) {
prototype[name] = prop[name];
}
function Class() {
this.construct.apply(this, arguments);
}
Class.prototype = prototype;
Class.prototype.constructor = Class;
Class.extend = extend;
return Class;
};
}) ();
Case of use:
var Person = Class.extend({
construct: function (name) {
this.name = name;
},
say: function () {
console.log('I am person: '+this.name);
},
});
var Student = Person.extend({
construct: function (name, mark) {
this.parent.construct.call(this, name);
this.mark = 5;
},
say: function () {
this.parent.say();
console.log('And a student');
},
getMark: function(){
console.log(this.mark);
}
});
var me = new Student('Alban');
me.say();
me.getMark();
console.log(me instanceof Person);
console.log(me instanceof Student);
Any opinion about this? I this way fast? What about correctness?
On a first sight, this looks not bad :), but I think the implementation done by coffeescript is currently one of the most sophisticated:
class Person
walk: ->
return
class myPerson extends Person
constructor: ->
super
translates to this:
var Person, myPerson,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
Person = (function() {
function Person() {}
Person.prototype.walk = function() {};
return Person;
})();
myPerson = (function(_super) {
__extends(myPerson, _super);
function myPerson() {
myPerson.__super__.constructor.apply(this, arguments);
}
return myPerson;
})(Person);
It is fast, clean and does ownProperty checking.
// Base state class -------------------------
function StateConstuctor()
{
}
// Inherited learn class --------------------
function StateLearnConstructor()
{
}
// Inherited exam class ---------------------
function StateExamConstructor()
{
}
function extend(Child, Parent)
{
var F = function() { }
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.superclass = Parent.prototype
}
function createState(rollType)
{
if (rollType == 'learn')
{
extend(StateLearnConstructor, StateConstuctor);
var state = new StateLearnConstructor();
return state;
}
else if (rollType == 'exam')
{
extend(StateExamConstructor, StateConstuctor);
var state = new StateExamConstructor();
return state;
}
}
StateConstuctor.prototype.getTitles = function()
{
console.log('base "virtual" function');
}
StateLearnConstructor.prototype.getTitles = function()
{
console.log('learn');
}
StateExamConstructor.prototype.getTitles = function()
{
console.log('exam');
}
Hello, I have the following "OOP" structure and I want to emulate something like virtual functions in C++. So I have base virtual function in StateConstructor and different realizations for each subclass.
var state = createState('exam');
state.getTitles();
But this code calls StateConstructor base virtual function. What's wrong here?
createState() is overwriting the prototypes for your StateLearnConstructor and your StateExamConstructor after you have assigned functions to them.
You shouldn't be conditionally extending them. Just extend them:
extend(StateLearnConstructor, StateConstuctor);
extend(StateExamConstructor, StateConstuctor);
StateConstuctor.prototype.getTitles = function () {
console.log('base "virtual" function');
};
StateLearnConstructor.prototype.getTitles = function () {
console.log('learn');
};
StateExamConstructor.prototype.getTitles = function () {
console.log('exam');
};
function createState(rollType) {
if (rollType == 'learn') {
return new StateLearnConstructor();
} else if (rollType == 'exam') {
return new StateExamConstructor();
}
}
Once you do that, your "virtual functions" should work as expected.
demo
Note: Your implementation for extend() is more complicated than it needs to be. The modern way to inherit a prototype is to use Object.create():
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.superclass = Parent.prototype;
}
What is the difference between extend methods in JavaScript?
Let's say we have the following classes:
var BaseClass = function() {
this.class_name = 'BaseClass';
this.foo = function() {
return 'foo';
}
this.sub = {
moreStuff: 'weeee'
}
};
BaseClass.prototype.bar = function () {
return 'To be or not to be';
}
var SubClass = function() {
this.class_name = 'SubClass';
this.bar = function() {
return 'bar';
}
this.sub = {
moreStuff: 'wooohooo'
}
};
Method A:
SubClass.prototype = new BaseClass();
SubClass.prototype.constructor = SubClass;
Method B (from underscore.js):
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
Method C (from LiveScript):
function extend$(sub, sup){
function fun(){}
fun.prototype = (sub.superclass = sup).prototype;
(sub.prototype = new fun).constructor = sub;
if (typeof sup.extended == 'function') sup.extended(sub);
return sub;
}
Method A looks simpler. Why go through the trouble of copying the object, one property at a time?
Yes, Method A looks simpler but using it you can inherit only from one object. What if you want your SubClass to inherit from BaseClassOther as well. In this case you should go for the Method B ( to inherit from BaseClassOther as well).
You can not do
SubClass.prototype = new BaseClassOther();
again this will overwrite prototype property.
Please have a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
In Method A, in other circumstances, BaseClass() might be written to require an argument and to fail if it does not receive a valid one. The other two methods are not bothered by this. Perhaps the inherited constructors you are working with are not bothered by this either.
If subclassing a "class" in JavaScript is done like so:
var ParentClass = function() {
// something
};
var ChildClass = function() {
// something
};
ChildClass.prototype = new ParentClass();
... what should I do when the parent class has required parameters?
var ParentClass = function(requiredParameter) {
if (typeof requiredParameter === 'undefined') {
throw new TypeError("'requiredParameter' is required!");
}
};
var ChildClass = function() {
// something
};
ChildClass.prototype = new ParentClass();
// ^ Throws TypeError
Thanks.
This is how its done:
function Parent( a ) {
this.a = a;
}
function Child( a, b ) {
Parent.call( this, a ); // this is crucial
this.b = b;
}
Child.prototype = Object.create( Parent.prototype );
Child.prototype.constructor = Child;
Live demo: http://jsfiddle.net/ECCgt/ (analyze the instances in the console)
The way you're doing it
ChildClass.prototype = new ParentClass();
is a dirty hack which is broken and should be avoided. Use Object.create to set up the inheritance relationship between the two prototype objects.
The second line
Child.prototype.constructor = Child;
is somewhat optional. We are correcting the constructor property because we had to overwrite Child.prototype in order to set up the inheritance. If you don't care about the constructor property, just leave out that line.
Subclass it like this instead:
function clone (obj) {
if (!obj) return;
clone.prototype = obj;
return new clone();
}
var ParentClass = function() {
// something
};
var ChildClass = function() {
// something
};
ChildClass.prototype = clone(ParentClass.prototype);
ChildClass.prototype.constructor = ChildClass; // if you want
Now you don't have to worry about it, because you don't have to call the parent constructor to subclass it :)
A better way to inherit...
var inherit = (function () {
var F = function () {}; // cache function
return function (C, P) { // Accepts Constructor and Parent
F.prototype = P.prototype;
// faster prototype chain lookup than direct instantiation
C.prototype = new F();
C._super = P.prototype;
C.prototype.constructor = C; // for checking instanceof
};
}());