Skip to content

Instantly share code, notes, and snippets.

@wzup
Forked from Maksims/class.js
Created January 19, 2017 18:18
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 wzup/18d8a44dcd73e1082bf28585b276dc31 to your computer and use it in GitHub Desktop.
Save wzup/18d8a44dcd73e1082bf28585b276dc31 to your computer and use it in GitHub Desktop.
Small implementation of classes with: extend, implement, and check methods
// base class
function Class() { }
// base class name
Class.prototype.className = 'Class';
// lists all parent classes and outputs JSON with it
Class.prototype.toString = function() {
var str = '';
var next = this.__proto__;
while(next && next.className) {
str = next.className + ':' + str;
next = next.__proto__;
}
return str.slice(0, -1) + ' ' + JSON.stringify(this);
};
// check if class implements specific interface
Class.prototype.implements = function(item) {
var next = this;
while(next) {
if (next.classImplementations) {
for(var i = 0, len = next.classImplementations.length; i < len; i++) {
if (next.classImplementations[i] == item) {
return true;
}
}
}
next = next.__proto__;
}
return false;
};
// check if class extends another class
Class.prototype.extends = function(item) {
return this instanceof item;
};
// check typeof class
Class.prototype.typeof = function(item) {
return item == Class;
};
// implement class extension
Class.extend = function(options) {
// name is mandatory
if (! options['name']) throw new Error('name option should be provided for a Class');
// check for reserved keys
var reserved = [
'super',
'typeof', 'extends', 'implements',
'className', 'classImplementations', 'classConstructor'
];
reserved.forEach(function(name) {
if (options[name]) throw new Error('could not extend class ' + options['name'] + ' - ' + name + ' is reserved key');
});
// keep in upper scope variables
var _super = this.prototype;
var className = options['name'];
var classImplementations = options['implement'];
var classConstructor = options['constructor'];
// class
function proto(args) {
args = args || { };
var i, len;
// if first, call own constructor as well
var first = ! args.__secondary;
args.__secondary = true;
// call parent constructor
_super.constructor.call(this, args);
// for each implementation of parent
if (_super.classImplementations) {
for(i = 0, len = _super.classImplementations.length; i < len; i++) {
// call its constructor
_super.classImplementations[i].call(this);
}
}
// if parent has consstructor, call it
if (_super.classConstructor) {
_super.classConstructor.call(this, args);
}
// if first
if (first) {
// check and call implementations
if (classImplementations) {
for(i = 0, len = classImplementations.length; i < len; i++) {
classImplementations[i].call(this, args);
}
}
// call own constructor
classConstructor.call(this, args);
}
}
// inherit prototype
proto.prototype = Object.create(this.prototype);
// implement
if (options['implement']) {
options['implement'].forEach(function(item) {
for(var name in item.prototype) {
proto.prototype[name] = item.prototype[name];
}
});
}
// set constructor
proto.prototype.constructor = proto;
// a way to call parents method
proto.prototype.super = function(method, args) {
if (! (args instanceof Array)) {
args = [ args ];
}
return _super[method].apply(this, args);
};
// check for typeof
proto.prototype.typeof = function(item) {
return item == proto;
};
// rename options
options['className'] = options['name'];
options['classConstructor'] = options['constructor'].name != 'Object' ? options['constructor'] : function() { };
options['classImplementations'] = options['implement'];
// remove garbage
delete options['name'];
delete options['constructor'];
delete options['implement'];
// implement own extensions
for(var name in options) {
proto.prototype[name] = options[name];
}
// allow class to be extended
proto.extend = Class.extend;
// return class
return proto;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment