Skip to content

Instantly share code, notes, and snippets.

@joubertnel
Last active December 25, 2015 18:49
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 joubertnel/7023795 to your computer and use it in GitHub Desktop.
Save joubertnel/7023795 to your computer and use it in GitHub Desktop.
Lazy parse a JavaScript class body
<html>
<body>
<div id="person1"></div>
<div id="person2"></div>
</body>
<script>
var util = {};
/**
* @class util.Class Provides simple class creation and inheritance
*
* Based on work from John Resig, base2, and Prototype. Class uses namespace
* safe method access allowing renaming of util.Class.
*
* Create an empty Class:
*
* var MyEmptyClass = util.Class.create();
*
* Create a typical Class instance:
*
* var MyClass = util.Class.create({
* init: function () {
* // This method serves as the constructor
* },
* aPrototypeMethod: function () {
* // All methods and properties are assigned to MyClass.prototype
* }
* });
*
* Extend a Class instance:
*
* var YourClass = MyClass.extend({
* init: function () {
* // Base class properties are overwritten. Base methods can be invoked
* // using the _super() method.
* this._super();
* }
* });
*
*/
(function(){
var _hasSuper = /\b_super\b/,
_doNotInit = {};
/**
* Enables sub classed methods to call their associated super class method
* using `this._super()`. Returns a function which executes in the scope
* of a Class instance.
* @param
* @param
* @returns {Function}
* @private
*/
function _bindSuper (value, superValue) {
// A _super() method is bound temporarily (created then destroyed) each
// time a subclassed method is run.
return function () {
var tmp = this._super,
ret;
this._super = superValue;
ret = value.apply(this, arguments);
// The method only need to be bound temporarily, so it is removed
// after the subclass method has executed.
if (tmp) {
this._super = tmp;
} else {
delete this._super;
}
return ret;
};
}
/**
* Extends a Class instance with properties to create a sub-class. Executes
* in scope of a Class instance.
* @public
* @param {Object} props Object descriptor with key/value pairs
* @returns {Function} A Class instance
*/
function _extend (props) {
var prototype = new this(_doNotInit),
name, getter, setter, value
;
// Copy the properties over onto the new prototype
for (name in props) {
getter = props.__lookupGetter__(name);
setter = props.__lookupSetter__(name);
if (getter || setter) {
getter && prototype.__defineGetter__(name, getter);
setter && prototype.__defineSetter__(name, setter);
} else {
value = props[name];
if (typeof value === "function" && _hasSuper.test(value)) {
// value: sub-class method
// this.prototype[name]: this class' (super class) method
value = _bindSuper(value, this.prototype[name] || function(){});
}
prototype[name] = value;
}
}
return _create(prototype);
}
/**
* Creates a new Class instance, optionally including a prototype
* object. This method is not applied to returned Class instances;
* use Class.extend to sub-class Class instances.
* @public
* @param {Object} props Object descriptor with key/value pairs
* @returns {Function} A Class instance
*/
function _create (props) {
var Class = function () {
var init = this.init;
// All construction is actually done in the init method
if (init && arguments[0] !== _doNotInit) {
init.apply(this, arguments);
}
};
// Populate our constructed prototype object
if (props) {
Class.prototype = props;
}
// Enforce the constructor to be what we expect
Class.constructor = Class;
// And make this class extendable
Class.extend = _extend;
return Class;
}
// Reveal _class publically
util.Class = {
create: _create
};
}());
function applyConstructor(ctor, args) {
var a = [];
for (var i = 0; i < args.length; i++)
a[i] = 'args[' + i + ']';
return eval('new ctor(' + a.join() + ')');
}
Person = function() {
var body = "util.Class.create({ init: function(name, address) { this.address = address; this.name = name; }, walk: function() { return 'walked'; } })";
var temp = eval(body),
result = applyConstructor(temp, arguments);
alert('now memoize - should only see once');
Person = temp;
return result;
};
/*
var Person = util.Class.create({
init: function(name, address) {
this.address = address;
this.name = name;
},
walk: function() {
return 'walked';
}
});
*/
var person1 = new Person('Sarah',
{street: 'blah', talk: function() { alert('boo'); }});
alert(person1.name);
alert(person1.address.street);
person1.address.talk();
var person1El = document.getElementById('person1');
var person2El = document.getElementById('person2');
person1El.innerHTML = person1.name + ' ' + person1.walk();
var person2 = new Person('Bubba');
person2El.innerHTML = '';
person2El.innerHTML = person2.name + ' ' + person2.walk();
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment