Last active
January 19, 2017 18:18
-
-
Save Maksims/8070893 to your computer and use it in GitHub Desktop.
Small implementation of classes with: extend, implement, and check methods
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
// 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
Usage example, shows how to extend classes as well as implement classic prototypes: