Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active April 25, 2024 13:20
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WebReflection/f1666535e732ae254c8666b783cbcefb to your computer and use it in GitHub Desktop.
Save WebReflection/f1666535e732ae254c8666b783cbcefb to your computer and use it in GitHub Desktop.
// now an npm package qith 100% code coverage:
// https://github.com/WebReflection/endow#endow---
// (c) Andrea Giammarchi, @WebReflection (ISC)
const mix = Super => ({
with: (...mixins) =>
mixins.reduce(
(Class, Mixin) => {
Class = Mixin(Class);
if (!Mixin.hasOwnProperty(Symbol.hasInstance)) {
Object.defineProperties(Mixin, {
// if only WeakSet were iterable ...
__class__: {value: []},
[Symbol.hasInstance]: {
configurable: true,
value(instance) {
return this.__class__.some(Class => instance instanceof Class);
}
}
});
}
(Mixin.__class__ || []).push(Class);
return Class;
},
Super
)
});
@WebReflection
Copy link
Author

WebReflection commented Mar 13, 2018

npm package

This is now a proper package you can install and use as needed.

Test/Coverage

const M1 = Super => class M1 extends Super {};
const M2 = Super => class M2 extends Super {};
class A {}
class B extends mix(A).with(M1) {}
class C extends mix(A).with(M2) {}
class D extends mix(A).with(M1, M2) {}

console.assert([
  new B instanceof A,
  new B instanceof M1,
  new C instanceof A,
  new C instanceof M2,
  new D instanceof A,
  new D instanceof M1,
  new D instanceof M2,
  !(new B instanceof M2),
  !(new C instanceof M1)
].every(Boolean));

Example

Symbol.emitter = Symbol('emitter');

const wm = new WeakMap;
const create = (self, dict) => (wm.set(self, dict), dict);

function Dict() {}
Dict.prototype = null;

const Emitter = Super => class extends Super {
  get [Symbol.emitter]() {
    return wm.get(this) || create(this, new Dict);
  }
  on(name, callback) {
    const emitter = this[Symbol.emitter];
    if (!(name in emitter)) {
      emitter[name] = new Set;
    }
    emitter[name].add(callback);
    return this;
  }
  removeListener(name, callback) {
    const emitter = this[Symbol.emitter];
    if (name in emitter) {
      emitter[name].delete(callback);
    }
    return this;
  }
  emit(name, ...args) {
    for (const fn of this[Symbol.emitter][name] || []) {
      fn.apply(this, args);
    }
  }
};


class MyParagraph extends mix(HTMLElement).with(Emitter) {}
customElements.define('my-p', MyParagraph);

const p = new MyParagraph;
p.on('test', console.log);
p.emit('test', 1, 2, 3);  // 1, 2, 3
p.removeListener('test', console.log);

p instanceof Emitter;     // true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment