Skip to content

Instantly share code, notes, and snippets.

@bosconian-dynamics
Last active October 31, 2017 22:42
Show Gist options
  • Save bosconian-dynamics/155f12895c901eef4d8d6766281567ac to your computer and use it in GitHub Desktop.
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
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