Last active
December 14, 2015 15:18
-
-
Save Skateside/39e526240f1065203a04 to your computer and use it in GitHub Desktop.
A handy function that creates a class in JavaScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var createClass = (function () { | |
'use strict'; | |
// Basic no-operation function | |
var noop = function () { | |
return; | |
}, | |
// Tests to see whether or not regular expressions can be called on | |
// Functions. | |
fnTest = (/return/).test(noop) | |
? (/[\.'"]\$super\b/) | |
: (/.*/); | |
// Basic function for looping over objects. | |
function forIn(obj, handler, context) { | |
Object.keys(obj).forEach(function (key) { | |
handler.call(context, key, obj[key]); | |
}); | |
} | |
// Basic function for extending one object with keys of another. | |
function augment(source, additional) { | |
forIn(additional, function (name, method) { | |
source[name] = method; | |
}); | |
return source; | |
} | |
/** | |
* Class.addMethod(name, method) | |
* - name (String): Name of the method to add. | |
* - method (Function): Function to add. | |
* | |
* Adds a method to the current class's `prototype`. As well as simple | |
* convenience, this method also handles the `$super` magic property. | |
* | |
* var Foo = createClass({ | |
* init: function (bar) { | |
* this.bar = bar; | |
* } | |
* }); | |
* | |
* var inst = new Foo(1); | |
* | |
* Foo.addMethod('getBar', function () { | |
* return this.bar; | |
* }); | |
* | |
* inst.getBar(); // -> 1 | |
* | |
**/ | |
function addMethod(name, method) { | |
var parent = this.parent; | |
this.prototype[name] = (typeof method === 'function' && | |
typeof parent[name] === 'function' && | |
fnTest.test(method)) | |
? function () { | |
var hasSuper = '$super' in this, | |
temp = this.$super, | |
ret = null; | |
this.$super = parent[name]; | |
ret = method.apply(this, arguments); | |
if (hasSuper) { | |
this.$super = temp; | |
} else { | |
delete this.$super; | |
} | |
return ret; | |
} | |
: method; | |
} | |
/** related to: Class.addMethod | |
* Class.addMethods(proto) | |
* - proto (Object): Key/Value pairs of names/methods to add. | |
* | |
* Helper function for adding multiple methods to a class's `prototype`. | |
**/ | |
function addMethods(proto) { | |
forIn(proto, this.addMethod, this); | |
} | |
/** | |
* Class.extend(name[, method]) | |
* - name (String|Object): Name of the method to add, or all methods to add. | |
* - method (Function): Function to add, if `name` is a `String`. | |
* | |
* Helper function either adding a single method to a class or multiple. | |
* Although using [[Class.addMethod]] or [[Class.addMethods]] may be | |
* slightly faster, this method can be more convenient. | |
**/ | |
function extendClass(name, method) { | |
if (name && typeof name === 'object') { | |
addMethods.call(this, name); | |
} else { | |
addMethod.call(this, name, method); | |
} | |
} | |
/** | |
* createClass([Base, ]proto) -> Class | |
* - Base (Function): Constructor to use as a base. | |
* - proto (Object): Prototype for the new class. | |
* | |
* Creates a new class. The `init` method is used as the constructor if it | |
* exists. | |
* | |
* var Foo = createClass({ | |
* init: function (bar) { | |
* this.bar = bar; | |
* } | |
* }); | |
* | |
* var inst = new Foo(1); | |
* inst.bar; // -> 1 | |
* | |
* Because `init` is essential for a class's operation, a basic | |
* "no-operation" will be added (see [[$f.noop]]). | |
* | |
* var Fii = createClass({ | |
* add5: function (n) { | |
* return n + 5; | |
* } | |
* }); | |
* | |
* var flop = new Fii(); | |
* flop.add5(5); // -> 10 | |
* | |
* To inherit from another class, simply provide a `Base`. | |
* | |
* var Bar = createClass(Foo, { | |
* getBar: function () { | |
* return this.bar; | |
* } | |
* }); | |
* | |
* var inst2 = new Bar(2); | |
* inst2.getBar(); // -> 2 | |
* | |
* When inheriting, all methods are granted access to a magical `$super` | |
* property. This property will call the parent's method and allow | |
* arguments to be passed through. | |
* | |
* var Baz = createClass(Bar, { | |
* | |
* init: function (bar, baz) { | |
* | |
* this.$super(bar); | |
* this.baz = baz; | |
* | |
* } | |
* | |
* }); | |
* | |
* var inst3 = new Baz(3, 4); | |
* inst3.getBar(); // -> 3 | |
* inst3.baz; // -> 4 | |
* | |
**/ | |
return function (Base, proto) { | |
// Base function for the new class. All new classes push everything into | |
// an init method. | |
function Class() { | |
return this.init.apply(this, arguments); | |
} | |
// Allow the Base to be optional. | |
if (!proto) { | |
proto = Base; | |
Base = Object; | |
} | |
// Expose a prototype extension method that enables the $super magic | |
// method. | |
augment(Class, { | |
addMethod: addMethod, | |
addMethods: addMethods, | |
extend: extendClass, | |
parent: Base.prototype | |
}); | |
// Inherit from Base. | |
Class.prototype = Object.create(Base.prototype); | |
// Add all methods to the new prototype. | |
addMethods.call(Class, proto); | |
// Basic constructor hack. | |
Class.prototype.constructor = Class; | |
// Allow a class to me made without a constructor function. | |
if (typeof Class.prototype.init !== 'function') { | |
Class.prototype.init = noop; | |
} | |
// Return the constructor. | |
return Class; | |
}; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment