Last active
November 29, 2018 23:19
-
-
Save matthewrobb/74b8e3f5ab5455c89f1ccbfb7e57c991 to your computer and use it in GitHub Desktop.
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() { | |
"use strict"; | |
// Utility class to enable extension of objects that cannot use | |
// normal class based inheritance e.g. custom functions | |
// | |
// Simply put, it adds a mixin static that contains all the property | |
// descriptors that class intends to pass on | |
class Mixable { | |
static mixin(target) { | |
if (!this.hasOwnProperty("mixin")) { | |
this.mixin = Object.assign( | |
Mixable.mixin.bind(this), | |
this.mixin, | |
Object.getOwnPropertyDescriptors(this.prototype) | |
); | |
} | |
return Object.defineProperties(target, this.mixin); | |
} | |
} | |
// Creates basic value storage functions | |
class BoxedValue extends Mixable { | |
static create(currValue) { | |
return this.init(function boxedValue(nextValue) { | |
return arguments.length > 0 ? (currValue = nextValue) : currValue; | |
}); | |
} | |
static init(box) { | |
return this.mixin(box); | |
} | |
} | |
// Building on BoxedValue this includes the concepts for using boxes as properties | |
// Since a box is just a function that takes 0 or 1 arguments this uses the box | |
// as both the setter and the getter for a property | |
class BoxedProperty extends BoxedValue { | |
// The box itself is also a property-descriptor which can be used in | |
// Object.defineProperty etc. | |
static init(box) { | |
return Object.assign(super.init(box), { | |
enumerable : true, | |
configurable : true, | |
get : box, | |
set : box | |
}); | |
} | |
// defines an own property on target(arg1) of name(arg2) which uses | |
// the box(arg3) as both the setter and the getter | |
// Ref: https://www.ecma-international.org/ecma-262/9.0/index.html#table-3 | |
static define(target, name, box = this.create()) { | |
if (!(box instanceof BoxedProperty)) { | |
this.init(box); | |
} | |
Object.defineProperty(target, name, box); | |
return box; | |
} | |
// This will look-up a BoxedProperty from a getter property of an object | |
static get(target, name) { | |
const { get } = Object.getOwnPropertyDescriptor(target, name) || false; | |
if (get instanceof BoxedProperty) { | |
return get; | |
} | |
} | |
// This will either return an existing BoxedProperty of an object or define one | |
static ensure(target, name, box) { | |
return this.get(...arguments) || this.define(...arguments); | |
} | |
// Link two BoxedProperties from the same or different objects | |
static link(source, name, target, targetName = name) { | |
return this.ensure(source, name).attach(target, targetName); | |
} | |
// Attach a boxed property to an object | |
attach(target, name) { | |
return this.constructor.define(target, name, this); | |
} | |
} | |
Object.assign( | |
typeof module !== "undefined" ? module["exports"] : window, | |
{ | |
Mixable, | |
BoxedValue, | |
BoxedProperty | |
} | |
); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment