Skip to content

Instantly share code, notes, and snippets.

@radist2s
Last active January 6, 2016 18:21
Show Gist options
  • Save radist2s/3d9a9e328e90c201ad33 to your computer and use it in GitHub Desktop.
Save radist2s/3d9a9e328e90c201ad33 to your computer and use it in GitHub Desktop.
JS Class traits
/**
* Usage:
* function MainClass() {}
* MainClass.prototype.delegate() {console.log('Main')}
* var methods = { delegate: function () { console.log('Sub') } }
* var SubClass = trait(MainClass, methods)
* new SubClass().delegate() // >>> Main, Sub
*/
define(function () {
var ids = {}
function createClassName(Class) {
var className = Class.name || 'ClassWrapper'
ids[className] = (ids[className] === undefined) ? 1 : ids[className] + 1
return className + ids[className]
}
function createClassSuperName(Class) {
var superClassBaseName = '__super',
baseId = 0,
superClassname = superClassBaseName
while (Class.prototype[superClassname] !== undefined) {
superClassname = superClassBaseName + (++baseId)
}
return superClassname
}
function createClassWrapper(Class) {
var classWrapperName = createClassName(Class)
var superClassName = createClassSuperName(Class)
var ClassWrapper = new Function(
'return function %wrapper%() { return this.%super%.apply(this, arguments) }'
.replace(/%wrapper%/i, classWrapperName)
.replace(/%super%/i, superClassName)
)()
ClassWrapper.prototype = Object.create(Class.prototype)
ClassWrapper.prototype[superClassName] = Class
ClassWrapper.prototype.constructor = ClassWrapper
return ClassWrapper
}
function createMethod(Class, name, func) {
return function () {
Class.prototype[name].apply(this, arguments)
return func.apply(this, arguments)
}
}
return function traitIt(Class, methods) {
if (!(Class instanceof Function) || !(methods instanceof Object)) {
return Class
}
var prototype = Class.prototype
var methodNames = Object.keys(methods)
var conflictMethods = []
methodNames.forEach(function (method) {
if (prototype[method] === undefined) {
prototype[method] = methods[method]
}
else if (prototype[method] !== methods[method]) {
conflictMethods.push(method)
}
})
if (!conflictMethods.length) {
return Class
}
var SubClass = createClassWrapper(Class)
conflictMethods.forEach(function (method) {
SubClass.prototype[method] = createMethod(Class, method, methods[method])
})
return SubClass
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment