goog.inherit Vs. simple inherit
JavaScript performance comparison
Info
Performance comparision between goog.inherit and John Resigs simple inherit, and tweaked version of it.
Based on this jsperf: http://jsperf.com/goog-inherits-vs-john-resig-extend/3
Preparation code
<script>
Benchmark.prototype.setup = function() {
var goog = {};
goog.inherits = function(childCtor, parentCtor) { /** @constructor */
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.superClass_ = parentCtor.prototype;
childCtor.prototype = new tempCtor(); /** @override */
childCtor.prototype.constructor = childCtor;
};
goog.base = function(me, opt_methodName, var_args) {
var caller = arguments.callee.caller;
if (caller.superClass_) {
// This is a constructor. Call the superclass constructor.
return caller.superClass_.constructor.apply(
me, Array.prototype.slice.call(arguments, 1));
}
var args = Array.prototype.slice.call(arguments, 2);
var foundCaller = false;
for (var ctor = me.constructor;
ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
if (ctor.prototype[opt_methodName] === caller) {
foundCaller = true;
} else if (foundCaller) {
return ctor.prototype[opt_methodName].apply(me, args);
}
}
// If we did not find the caller in the prototype chain,
// then one of two things happened:
// 1) The caller is an instance method.
// 2) This method was not called by the right caller.
if (me[opt_methodName] === caller) {
return me.constructor.prototype[opt_methodName].apply(me, args);
} else {
throw Error('goog.base called from a method of one name ' + 'to a method of a different name');
}
};
var fnTest = /xyz/.test(function() {
xyz;
}) ? /\b_super\b/ : /.*/;
Class = function() {};
Class.extend = function extend(prop) {
var _super = this.prototype;
function Child() {
if (this.init) this.init.apply(this, arguments);
}
Child.prototype = Object.create(this.prototype);
Child.prototype.constructor = Child;
Child.extend = extend;
for (var name in prop) {
// Check if we're overwriting an existing function
if (typeof prop[name] == "function" && typeof _super[name] == "function" && /\b_super\b/.test(prop[name])) {
Child.prototype[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, prop[name]);
} else {
Child.prototype[name] = prop[name];
}
}
return Child;
}
ClassJR = function() {};
// Create a new Class that inherits from this class
ClassJR.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[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, prop[name]) : prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing && this.init) this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
ClassSG = function() {};
// Create a new Class that inherits from this class
ClassSG.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
var Surrogate = function() {
this.constructor = {};
};
Surrogate.prototype = this.prototype;
prototype = new Surrogate;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[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, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (this.init)
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
BBClass = function(ctor) {
ctor.prototype = {};
ctor.prototype.constructor = ctor;
ctor.extend = 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;
};
var googA = function() {
this.a = 'a';
this.b = 'b';
this.c = this.a + this.b;
};
googA.prototype.foo = function() {
return "foo"
};
googA.prototype.hello = function() {
return "hello";
};
googA.prototype.foobar = function() {
return "foobar";
};
googA.prototype.foobar2 = function() {
return "foobar2";
};
var googB = function() {
googA.call(this);
};
goog.inherits(googB, googA);
googB.prototype.foo = function() {
return googA.prototype.foo.call(this);
};
googB.prototype.bar = function() {
return "bar";
};
googB.prototype.foobar = function() {
return googB.superClass_.foobar.call(this);
};
googB.prototype.foobar2 = function() {
return googA.prototype.foobar2.call(this);
};
var extendA = Class.extend({
init: function() {
//this is constructor
this.a = 'a';
this.b = 'b';
this.c = this.a + this.b;
},
foo: function() {
return "foo";
},
hello: function() {
return "hello";
},
foobar: function() {
return "foobar";
}
});
var extendB = extendA.extend({
init: function() {
this._super();
},
foo: function() {
this._super();
},
bar: function() {
return "bar";
},
foobar: function() {
return extendA.prototype.foobar.call(this)
}
});
var extendJRA = ClassJR.extend({
init: function() {
//this is constructor
this.a = 'a';
this.b = 'b';
this.c = this.a + this.b;
},
foo: function() {
return "foo";
},
hello: function() {
return "hello";
},
foobar: function() {
return "foobar";
}
});
var extendJRB = extendJRA.extend({
init: function() {
this._super();
},
foo: function() {
this._super();
},
bar: function() {
return "bar";
},
foobar: function() {
return extendJRA.prototype.foobar.call(this)
}
});
var extendSGA = ClassSG.extend({
init: function() {
//this is constructor
this.a = 'a';
this.b = 'b';
this.c = this.a + this.b;
},
foo: function() {
return "foo";
},
hello: function() {
return "hello";
},
foobar: function() {
return "foobar";
}
});
var extendSGB = extendSGA.extend({
init: function() {
this._super();
},
foo: function() {
this._super();
},
bar: function() {
return "bar";
},
foobar: function() {
return extendSGA.prototype.foobar.call(this)
}
});
var bbClassA = BBClass(function() {
//this is constructor
this.a = 'a';
this.b = 'b';
this.c = this.a + this.b;
});
bbClassA.prototype = {
foo: function() {
return "foo";
},
hello: function() {
return "hello";
},
foobar: function() {
return "foobar";
}
};
var bbClassB = BBClass(function() {}).extend(bbClassA);
bbClassB.prototype.bar = function() {
return "bar";
};
bbClassB.prototype.foobar = function() {
return bbClassA.prototype.foobar.call(this);
};
function objA() {
//this is constructor
this.a = 'a';
this.b = 'b';
this.c = this.a + this.b;
};
objA.prototype = {
foo: function() {
return "foo";
},
hello: function() {
return "hello";
},
foobar: function() {
return "foobar";
}
};
function objB() {};
objB.prototype = Object.create(objA.prototype);
objB.prototype.bar = function() {
return "bar";
};
objB.prototype.foobar = function() {
return objA.prototype.foobar.call(this);
};
fpAprototype = {
foo: function() {
return "foo";
},
hello: function() {
return "hello";
},
foobar: function() {
return "foobar";
}
};
function createFPA() {
var instance = Object.create(fpAprototype);
//this is constructor
instance.a = 'a';
instance.b = 'b';
instance.c = this.a + this.b;
return instance;
}
var fpBprototype = Object.create(fpAprototype);
fpBprototype.bar = function() {
return "bar";
};
fpBprototype.foobar = function() {
return fpAprototype.prototype.foobar.call(this);
};
function createFPB() {
return Object.create(fpBprototype);
}
};
</script>
Test runner
Warning! For accurate results, please disable Firebug before running the tests. (Why?)
Java applet disabled.
| Test | Ops/sec | |
|---|---|---|
1.1 goog.inherit define classes |
|
pending… |
1.2 Extend define classes |
|
pending… |
1.3 John Resig Extend define classes |
|
pending… |
1.4 Surrogate Extend define |
|
pending… |
2.1 Goog inherit object creation |
|
pending… |
2.2 Extend object creation |
|
pending… |
2.3 JR Extend object creation |
|
pending… |
2.4 Surrogate object creation |
|
pending… |
1.5 bennybennet define class |
|
pending… |
1.5.2 bennybennet define class (cached functions) |
|
pending… |
2.5 bennybennet object creation |
|
pending… |
1.6 Prototypal object define |
|
pending… |
2.6 Prototypal object creation |
|
pending… |
1.7 Factory Pattern object define |
|
pending… |
2.7 Factory Pattern object creation |
|
pending… |
Compare results of other browsers
Revisions
You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:
- Revision 1: published by Benny Bennet
- Revision 2: published by Benny Bennet
- Revision 3: published by Benny Bennet
- Revision 4: published by Benny Bennet and last updated
- Revision 5: published by Benny Bennet
- Revision 6: published by Benny Bennet
0 comments