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.
Related
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
i am just starting learn a prototypes in javascript an i can't understand what a problem is in my code. I'm sorry, my question may seem silly
i got an error like this :
Uncaught TypeError: undefined is not a function
why is undefined? I inherited a function of the user.
can't understand it :(
var user = {
sayName: function() {
console.log(this.name)
}
};
user.name = "viktor";
user.sayName();
var user2 = {};
user2.prototype = user;
user2.name = "segey";
user2.sayName();
All you need to set up the prototype chain with plain objects is:
var user2 = Object.create(user); // set `user` as prototype of `user2`
user2.name = "segey";
user2.sayName();
For you question correct solution will:
function User() {
this.name = 'Viktor';
return this;
}
User.prototype = Object.create({
sayName: function() {
return this.name;
}
});
function User2() {}
User2.prototype = Object.create(User.prototype);
var user = new User();
user.sayName(); // 'Viktor'
user2 = new User2();
user2.name = 'Bogdan';
user2.sayName(); // 'Bogdan'
And detailed explanation with example.
Let say we have some basic class Animal. Our Animal have age and name.
function Animal() {
this.age = 5;
this.name = "Stuffy";
return this;
}
Animal.prototype = Object.create({
getAge: function() {
return this.age;
},
getName: function() {
return this.name;
}
});
And when I spent some time with architecture I understand that I also need SubClass of Animal. For example, let it will be Dog class with new property and functions. And also Dog must extend functions and properties from Animal class.
function Dog() {
Animal.apply(this, arguments); // Call parent constructor
this.wantsEat = true; // Add new properties
return this;
}
Dog.prototype = Object.create(Animal.prototype); // Create object with Animal prototype
Object.extend(Dog.prototype, { // Any extend() function, wish you want
constructor: Dog, // Restore constructor for Dog class
eat: function() {
this.wantsEat = false;
return this;
}
});
Or you can use Object.defineProperties() and extend in this way:
Dog.prototype = Object.create(Animal.prototype, {
constructor: {
value: Dog
},
eat: {
value: function() {
this.wantsEat = false;
return this;
}
}
});
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.
I'm looking for a method to create nested js-objects of the same type with property-fallbacking.
I'd like to be able to write for instance:
state.isLoggedIn and if this object doesn't have this property (undefined) then it should look in a base-state etc until no base-states exists anymore, then return undefined.
With base-state I mean some other state that this state is based on, not inherited like is class inheritance.
I was thinking of making some kind of class like this:
function State(base) {
this.base = base; // also a state
}
When I try to get a property P from a state A that is based on another state B, and state A doesn't define a property P it should go look in state B instead.
I know I could use a function like state.getState(key) that looks in its own properties first and then in the base-properties. But I'm looking for a method of doing this with normal properties instead.
In C# it would look something like this (and making it a dynamic object I would get almost excatly the same ducked typed state I'm looking for in javascript):
class State
{
public State(State base)
{
_base = base;
}
State _base;
Dictionary<string, object> _values = new Dictionary<string, object>();
public object this[string key]
{
get { return _values.ContainsKey(key) ? _values[key] : _base[key]; }
set { _values[key] = value; }
}
}
Any ideas? Possible?
UPDATE:
This is what I got now:
function State() {
}
function ViewModelBase() {
var self = this;
self.__parent = ko.observable(null);
self.state = new State();
self.parent = ko.computed({
read: function () {
return self.__parent();
},
write: function (value) {
if (getObjectClass(value) !== "ViewModelBase") throw new Error("Wrong type of parent.");
var p = self.__parent();
if (p != null) throw new Error("Allready parented.");
self.__parent(value);
// here i'd like to inherit/nest self.state with value.state
}
});
}
Not sure if this is what you're looking for, but maybe it is:
var state1 = {a : 1};
var state2 = Object.create(state1);
state2.b = 2;
console.log(state2.a); // 1
var state3 = Object.create(state2);
state3.a = 10; // creates an own "a" in state3
console.log(state1.a); // 1
console.log(state2.a); // 1
console.log(state3.b); // 2
This is using inheritance, as I suggested in my original comment to your question. Object.create returns a new object that uses the object passed as the first argument as its [[Prototype]] (which some implementations expose via the __proto__ property). When you try to access a property of the new object and an own property is not found, it looks up in the prototype chain.
Object.create is not supported by older browsers, but a very simple polyfill is available on MDN.
This is what CoffeeScript uses for class extenstions (using prototype inheritance):
var __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;
};
This assumes that the class functions are all a part of the prototype of the class functions.
For example:
Animal = (function() {
function Animal() {}
Animal.prototype.name = 'Generic Animal';
Animal.prototype.my_sound = 'none';
Animal.prototype.sound = function() {
return console.log(this.my_sound);
};
return Animal;
})();
Cow = (function(_super) {
__extends(Cow, _super);
function Cow() {
return Cow.__super__.constructor.apply(this, arguments);
}
Cow.prototype.name = 'Cow';
Cow.prototype.my_sound = 'Moo';
return Cow;
})(Animal);
Cat = (function(_super) {
__extends(Cat, _super);
function Cat() {
return Cat.__super__.constructor.apply(this, arguments);
}
Cat.prototype.name = 'Cat';
Cat.prototype.my_sound = 'Meow';
return Cat;
})(Animal);
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
};
}());