Skip to content

Instantly share code, notes, and snippets.

@bennybennet
Last active December 11, 2015 11:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennybennet/4593122 to your computer and use it in GitHub Desktop.
Save bennybennet/4593122 to your computer and use it in GitHub Desktop.
Trying to implement a usefull and easy-to-read way for declaring classes in JavaScript.
/* Simple JavaScript Inheritance
* First draft!
* @author Benny Bennet Jürgens (http://ben.nyben.net)
* CC-by-sa 3.0 - please provide my name and url
*/
;(function() {
"use strict";
this.BBClass = function(ctor, proto) {
ctor.prototype = proto || {};
ctor.prototype.constructor = ctor;
ctor.extends = function(parent) {
function ChildBB() {
parent.prototype.constructor.apply(this, arguments);
ctor.apply(this, arguments);
};
ChildBB.prototype = ( typeof Object.create !== 'function') ?
(function(parent) {
function F() {};
F.prototype = parent.prototype;
var prototype = new F();
prototype.constructor = ChildBB;
return prototype;
})(parent)
: Object.create(parent.prototype, { constructor: { value: ChildBB, enumerable: false }});
return ChildBB;
}
return ctor;
}
}).call(this);
var Person = BBClass(function(isDancing) {
console.log("person");
this.dancing = isDancing;
});
Person.prototype.dance = function() {
return this.dancing;
};
var Ninja = BBClass(function(isDancing) {
console.log("ninja");
}).extends(Person);
Ninja.prototype.swingSword = function() {
return true;
};
Ninja.prototype.arraytest = [1,2,3,4];
var p = new Person(true);
var n = new Ninja(false);
Person.prototype.test2 = function() { console.log("test2 bestanden"); };
console.log(n.arraytest);
console.log(p.dance());
console.log(n.dance());
console.log(n.swingSword())
p.test2();
n.test2();
console.log("inherit ok?")
console.log( p instanceof Object);
console.log( p instanceof Person);
console.log( p instanceof Person && p instanceof Object && n instanceof Ninja && n instanceof Person && n instanceof Object);
=> person
=> person
=> ninja
=> [1,2,3,4]
=> true
=> false
=> true
=> test2 bestanden
=> test2 bestanden
=> inherit ok?
=> true
=> true
=> true
@bennybennet
Copy link
Author

Because we don't want to execute the construtor of the base class, John Resig had a boolean in the constructor to check wether the init should be executed or not. I replaced this in the first or second revision of this gist with a surrogate-pattern, which i found in the backbonejs source. As far as i can see, this wouldn't improve the performance, but in my opinion the constructor is more clean. I tried to play with "proto" in the third or fourth revision in hope to gain some performance due to the lack of having a temp object.
Unfortunately this isn't supported by IE and not recommended to use by MDN either, so i found myself googleing more. I found this "Object.create" thingy in the MDN which seems to fit my needs. Later i found the following article explaining "Object.create" quite well: http://dailyjs.com/2012/06/04/js101-object-create/

Tested around on jsPerf quite a while http://jsperf.com/goog-inherit-vs-simple-inherit. Finally understood the way Johns extend-function does his job. "Object.create" seems 33% slower than "new" on defining the Object, around 5% faster on creating these Objects though. The most performance loss is on

fnTest.test(prop[name])

which tests if the function has a "_super" call inside. Depending on

var fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

"fnTest.test()" is always true, if the browser don't support function decompilation (so function.toString()). If we would simply remove the "fnTest.test()" to gain performance, every function would be:

function() {
  var tmp = this._super;
  this._super = _super[name];
  var ret = fn.apply(this, arguments);
  this._super = tmp;
  return ret;
};

Whats the drawback here? Do we have another possibility to handle "_super" calls?

@bennybennet
Copy link
Author

Spent some more hours working on this. Does it realy make sense to go on with this?

After reading this http://javascript.crockford.com/prototypal.html and http://javascript.crockford.com/inheritance.html and especialy the paragraph

"I have been writing JavaScript for 8 years now, and I have never once found need to use an uber function. The super idea is fairly important in the classical pattern, but it appears to be unnecessary in the prototypal and functional patterns. I now see my early attempts to support the classical model in JavaScript as a mistake." (Douglas Crockford)

I came up with the idea you can see in revision 8. For now i skipped the whole super-thing, instead the child-constructor is calling the parent-constructor first and then himself (this is based on super() Java).

The performance so far is outstanding, even though i'm using Object.create. As you can see here http://jsperf.com/goog-inherit-vs-simple-inherit/2 it's a massive 100% faster in object creation and round about 30% faster for the object definition compared to the champion goog.inherit.
I'm not a JavaScript Expert which leads me to an uncomfortable situation: I'm not quite sure if i didn't make a mistake.

@bennybennet
Copy link
Author

Improved performance of object definition and addes mixins (aka jQuery.extend - WIP). Latest performance measures: http://jsperf.com/goog-inherit-vs-simple-inherit/6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment