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 }