Last active
April 8, 2019 16:12
-
-
Save neeravp/c00f1e66d2fb06673c00af0ddf1822e6 to your computer and use it in GitHub Desktop.
Function to extend a class by mixing properties and methods from other objects or classes - composition not inheritance
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
function cmixin(target, ...sources) { | |
if(typeof target === 'function') { | |
sources.forEach(source => { | |
//When composing from an object | |
if(typeof source === 'object') { | |
Object.getOwnPropertyNames(source).forEach(prop => { | |
let descriptor = Object.getOwnPropertyDescriptor(source, prop); | |
Object.defineProperty(target, prop, descriptor); | |
}); | |
Object.getOwnPropertySymbols(source).forEach(symbol => { | |
let descriptor = Object.getOwnPropertyDescriptor(source, symbol); | |
if(descriptor.enumerable) { | |
Object.defineProperty(target, symbol, descriptor); | |
} | |
}); | |
} | |
//When composing from another class | |
else if(typeof source === 'function') { | |
//Get all instance properties and methods | |
Object.getOwnPropertyNames(source.prototype).forEach(prop => { | |
let descriptor = Object.getOwnPropertyDescriptor(source.prototype, prop); | |
//Do not copy constructor and readonly properties like name, length, prototype etc | |
if(descriptor.writable && prop !== 'constructor') { | |
//To allow overriding of instance methods remove this if | |
if(target.prototype[prop] === undefined) { | |
Object.defineProperty(target.prototype, prop, descriptor); | |
} | |
} | |
//To copy the instance properties defined in the source class constructor | |
//Don't think it is a good idea cause source constructor may have different set of args | |
// if(prop === 'constructor') { | |
// let instance = new source() | |
// Object.keys(instance).forEach(iprop => { | |
// if(target.prototype[iprop] === undefined){ | |
// target.prototype[iprop] = instance[iprop] | |
// } | |
// }); | |
// } | |
}); | |
//Get all static properties and methods | |
Object.getOwnPropertyNames(source).forEach(prop => { | |
let descriptor = Object.getOwnPropertyDescriptor(source, prop); | |
//Do not copy the readonly properties like name, length, prototype | |
if(descriptor.writable) { | |
//To allow overriding of the static properties remove this if | |
if(target[prop] === undefined) { | |
Object.defineProperty(target, prop, descriptor); | |
} | |
} | |
}); | |
} | |
}); | |
} | |
return target; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Couldn't figure out a way to mixin getters and setters from one class to another.
Update:
Have factored a way to mixin getters and setters from other objects as well as classes to the target class.
Not sure if its the best approach though