Skip to content

Instantly share code, notes, and snippets.

@ZempTime
Created May 13, 2020 19:57
Show Gist options
  • Save ZempTime/f2a5668c61544f7524e89aeebb92e783 to your computer and use it in GitHub Desktop.
Save ZempTime/f2a5668c61544f7524e89aeebb92e783 to your computer and use it in GitHub Desktop.
Mixin Exploration
// Following along withhttps://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/
// Basic Mixin Implementation
const MyMixin = (superclass: any) =>
class extends superclass {
foo() {
console.log("foo from MyMixin");
}
you() {
if (super.you) {
super.you();
}
console.log("you from MyMixin");
}
};
class MyBaseClass {
you() {
console.log("you from MyBaseClass");
}
}
class MyClass extends MyMixin(MyBaseClass) {}
const newMyClass = new MyClass();
newMyClass.foo();
newMyClass.you();
console.log("---");
// Works on multiple
const MyMixin2 = (superclass: any) =>
class extends superclass {
moo() {
console.log("moo from MyMixin2");
}
};
class CompoundMixin extends MyMixin(MyMixin2(MyBaseClass)) {}
const compoundMixin = new CompoundMixin();
compoundMixin.foo();
compoundMixin.moo();
console.log("---");
// Better Syntax
let mix = (superclass: any) => new MixinBuilder(superclass);
class MixinBuilder {
superclass: any;
constructor(superclass: any) {
this.superclass = superclass;
}
with(...mixins: any) {
return mixins.reduce((c: any, mixin: any) => mixin(c), this.superclass);
}
}
class MyClass3 extends mix(MyBaseClass).with(MyMixin, MyMixin2) {}
const myClass3 = new MyClass3();
myClass3.you();
myClass3.foo();
console.log("---");
class A extends mix(MyBaseClass).with(MyMixin) {}
class B extends mix(MyBaseClass).with(MyMixin) {}
let a = new A();
let b = new B();
console.log(a instanceof A);
console.log(b instanceof B);
// not yet cached
console.log(Object.getPrototypeOf(a) === Object.getPrototypeOf(b));
const Mixin = (mixin) => Cached(HasInstance(BaseMixin(mixin)));
const MyMixin = Mixin(
(superclass) =>
class extends superclass {
/* ... */
}
);
const _originalMixin = Symbol("_originalMixin");
const _mixinRef = Symbol("_mixinRef");
const wrap = (mixin, wrapper) => {
Object.setPrototypeOf(wrapper, mixin);
if (!mixin[_originalMixin]) {
mixin[_originalMixin] = mixin;
}
return wrapper;
};
const BaseMixin = (mixin) =>
wrap(mixin, (superclass) => {
let application = mixin(superclass);
application.prototype[_mixinRef] = mixin[_originalMixin];
return application;
});
const Cached = (mixin) =>
wrap(mixin, (superclass) => {
// Create a symbol used to reference a cached application from a superclass
let applicationRef = mixin[_cachedApplicationRef];
if (!applicationRef) {
applicationRef = mixin[_cachedApplicationRef] = Symbol(mixin.name);
}
// Look up a cached application of `mixin` to `superclass`
if (superclass.hasOwnProperty(applicationRef)) {
return superclass[applicationRef];
}
// apply the mixin
let application = mixin(superclass);
// Cache the mixin application on the superclass
superclass[applicationRef] = application;
return application;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment