Last active
October 31, 2017 22:42
-
-
Save bosconian-dynamics/155f12895c901eef4d8d6766281567ac to your computer and use it in GitHub Desktop.
Basic class framework for hackmud. Inheritance is implemented by copying property data and binding methods in lieu of creating prototype chains
This file contains hidden or 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() { | |
if( #FMCL ) | |
return #G.class | |
// Opinionated deep clone/merge. Copies the right-most primative into target | |
const deepMerge = ( target, ...sources ) => { | |
if( !sources.length ) | |
return target | |
let obs = [] | |
sources.unshift( target ) | |
for( let i = sources.length; i > 0; i-- ) { | |
if( sources[ i ] === undefined ) | |
continue | |
if( typeof sources[ i ] != 'object' || sources[ i ] === null || sources[ i ] instanceof Array ) { | |
if( !obs.length ) | |
obs.push( sources[ i ] ) | |
break | |
} | |
obs.push( sources[ i ] ) | |
} | |
if( !obs.length ) | |
return target | |
let props = obs.reduce( ( props, obj ) => { | |
if( !obj ) | |
return props | |
for( let prop of Object.keys( obj ) ) { | |
if( !props.includes( prop ) ) | |
props.push( prop ) | |
} | |
return props | |
}, [] ) | |
return Object.assign( | |
target, | |
props.reduce( ( mob, prop ) => { | |
mob[ prop ] = deepMerge.call( null, ...obs.map( o => o[ prop ] ) ) | |
return mob | |
}, {} ) | |
) | |
} | |
// Methods existing on every Class instance object. | |
// The first argument will be bound to the instance object itself | |
const instance_methods = { | |
// Check if a Class instance object is descendant from a specific Class object | |
instanceOf( instance, other ) { | |
if( other.__name ) | |
other = other.__name | |
else if( 'string' != typeof other ) | |
throw Error( 'Invalid class comparison predicate (should be a class object or name): ' + other ) | |
return instance.__class.__path.find( cl => cl._name == other ) | |
}, | |
// Call ancestor constructors | |
super( instance, ...args ) { | |
if( !instance.__super_index ) | |
instance.__super_index = 2 | |
const parent = instance.__class.__path[ instance.__class.__path.length - instance.__super_index++ ] | |
if( !parent ) | |
throw new Error('Invalid super call: no parent Class object') | |
parent.__constructor.call( null, instance, ...args ) | |
if( !parent.__parent ) | |
delete instance.__super_index | |
} | |
} | |
// Methods existing on every Class object. | |
// The first argument will be bound to the Class object itself | |
const class_methods = { | |
// Extend a class by copying method and prop definitions | |
extend( parent, name, con = () => {}, props = {}, methods = {}, stat = {} ) { | |
let __class = { // Class definition | |
__constructor: con, | |
__parent: parent, | |
__path: [], | |
__props: parent | |
? Object.assign( {}, parent.__props, props ) | |
: Object.assign( {}, props ), | |
__name: name, | |
__methods: parent | |
? Object.assign( {}, parent.__methods, methods ) | |
: Object.assign( {}, methods ) | |
} | |
// Shim the constructor and apply definition data | |
__class = Object.assign( | |
( ...args ) => class_methods.instantiate( __class, ...args ), | |
__class, | |
stat // Add supplied static properties and methods. | |
) | |
// Add Class ancestory array | |
__class.__path = parent ? parent.__path.concat( __class ) : [ __class ] | |
// Bind class object methods (extend(), instantiate(), etc.) | |
for( let name in class_methods ) | |
__class[ name ] = class_methods[ name ].bind( null, __class ) | |
return __class | |
}, | |
// Create an instance of a class | |
instantiate( __class, ...args ) { | |
let instance = deepMerge( {}, __class.__props ) // Deep copy class properties/defaults | |
for( let name in __class.__methods ) | |
instance[ name ] = __class.__methods[ name ].bind( null, instance ) // Bind class methods to instance | |
for( let name in instance_methods ) | |
instance[ name ] = instance_methods[ name ].bind( null, instance ) // Bind common instance methods | |
instance.__class = __class | |
// If instantiating an extended class, bind parent methods as properties on instance.super | |
if( __class.__parent ) { | |
instance.super = Object.assign( | |
instance.super, | |
Object.keys( __class.__parent.__methods ).reduce( | |
( methods, name ) => { | |
methods[ name ] = __class.__parent.__methods[ name ].bind( null, instance ) | |
return methods | |
}, | |
{} | |
) | |
) | |
} | |
// Call the constructor and return the instance (or truthy constructor return value) | |
return __class.__constructor.call( null, instance, ...args ) || instance | |
} | |
} | |
// Class factory | |
const Class = class_methods.extend.bind( null, null ) | |
#G.class = Class | |
return Class | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment