3. What is it?
• It’s a small JavaScript library
• It was written by John Resig,
creator of jQuery, for his book
"Secrets of the JavaScript
Ninja"
• It provides a simplified API for
Class-like inheritance in
JavaScript
• Can be found at John’s site or
on NPM
4. Why is it interesting?
• It’s only 25 lines long (without
comments)
• It baffled me
• It’s very cleverly written
• It’s a bit of an anti-pattern
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /b_superb/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
Class.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;
};
})();
8. Usage
• Create an object to represent your prototype
• Pass the object to Class.extend to get a constructor for your new class
• The “extend” method is attached to the new constructor
• Pass a new partial prototype to constructor’s “extend” method to extend the
class
13. Wrapped in a self-executing function expression
(function(){
// the magic goes in here
})();
14. Initializing, and the function test
var initializing = false,
fnTest = /xyz/.test(function(){xyz;}) ? /b_superb/ : /.*/;
15. The base constructor does nothing but show up in
your browser console
// The base Class implementation (does nothing)
this.Class = function(){};
16. Define the “extend” method “statically”
// Create a new Class that inherits from this class
Class.extend = function(prop) {/*...*/}
17. Get a reference to the parent prototype, create an
instance to serve as a new prototype
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;
18. Copy the property definition into the prototype
// 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]) ?
/* ... super things happen here */ :
prop[name];
}
19. If prop[name] is a function containing “_super”...make
“_super” a reference to the parent method
(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])
20. One more time, in slow motion
• If the property to be copied is a function…
• And contains the text “_super”…
• Create a closure which returns a function…
• Which caches anything called “_super” temporarily…
• Then stores a reference to the parent prototype’s function of the same name…
• Then calls the original (not _super) function…
• Uncaching the local copy of “_super”…
• And return the results of the function
21. Create a new constructor which calls “init”
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
22. Assign the augmented prototype to the constructor
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
23. Attach “extend” to the new constructor, and return
the new class
// And make this class extendable
Class.extend = arguments.callee;
return Class;
26. You can create “types” in JavaScript with
constructors and instances
But JavaScript types do not have a guaranteed interface.
27. • All objects can be modified regardless of type (the constructor)
• Object API can change at any time addition or deletion of properties
• Types have no guaranteed interface, only a prototype chain
• You have to test the api of unknown objects
In JavaScript…
29. Remember
• Creating an object in JavaScript is trivial
• Copying objects in JavaScript is trivial
• In most cases, most objects in JavaScript are singletons
• Singletons can "inherit" by using a mix-in pattern
• _.assign/extend
• jQuery.extend
{}
for (x in y) {
" z[x] = y[x];
}
30. When do you need Class.js?
• When you must define multiple types of an object which…
• Share a common, inherited API, which…
• Needs access to the parent API
31. Examples…
• User management in an application with many types of users (employees,
managers, space pirates)
• Product listing with a base "product" definition inherited product types where
all products share common features / API