Skip to content

Instantly share code, notes, and snippets.

@matthewrobb
Last active November 29, 2018 23:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewrobb/74b8e3f5ab5455c89f1ccbfb7e57c991 to your computer and use it in GitHub Desktop.
Save matthewrobb/74b8e3f5ab5455c89f1ccbfb7e57c991 to your computer and use it in GitHub Desktop.
;(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