Created
May 13, 2020 19:57
-
-
Save ZempTime/f2a5668c61544f7524e89aeebb92e783 to your computer and use it in GitHub Desktop.
Mixin Exploration
This file contains hidden or 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
// 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