<NME> mixwith.js
<BEF> 'use strict';

export const _cachedApplicationRef = Symbol('_cachedApplicationRef');
export const _mixinRef = Symbol('_mixinRef');
export const _originalMixin = Symbol('_originalMixin');

/**
 * Applies `mixin` to `superclass`.
 *
 * @example
 * const M = (superclass) => class extends superclass {
 */
export const apply = (superclass, mixin) => {
  let application = mixin(superclass);
  application.prototype[_mixinRef] = unwrap(mixin);
  return application;
};

 * @return {Function} A subclass of `superclass`
 */

/**
 * Applies `mixin` to `superclass`.
 * as created by `apply`.
 */
export const isApplicationOf = (proto, mixin) =>
  proto.hasOwnProperty(_mixinRef) && proto[_mixinRef] === unwrap(mixin);

/**
 * Sets up the function `mixin` to be wrapped by the function `wrapper`, while
 * // M now works with `hasMixin` and `isApplicationOf`
 * const M = Applier((superclass) => class extends superclass {});
 *
 * class C extends M(Object) {}
 * let i = new C();
 * hasMixin(i, M); // true
 *
 * @function
 */
export const wrap = (mixin, wrapper) => {
  Object.setPrototypeOf(wrapper, mixin);
  if (!mixin[_originalMixin]) {
    mixin[_originalMixin] = mixin;
  }
  return wrapper;
};
};

/**
 * one or more calls to `wrap`. Returns `wrapper` if it's not a wrapped
 * function.
 */
export const unwrap = (wrapper) => wrapper[_originalMixin] || wrapper;


/**
 * @param {Object} proto A prototype object created by {@link apply}.
 * @param {MixinFunction} mixin A mixin function used with {@link apply}.
 * @return {boolean} whether `proto` is a prototype created by the application of
 * `mixin` to a superclass
 */
export const isApplicationOf = (proto, mixin) =>
  proto.hasOwnProperty(_appliedMixin) && proto[_appliedMixin] === unwrap(mixin);

  return false;
}

/**
 * Decorates `mixin` so that it caches its applications. When applied multiple
 * times to the same superclass, `mixin` will only create one subclass, memoize
 * chain
 */
export const hasMixin = (o, mixin) => {
  while (o != null) {
    if (isApplicationOf(o, mixin)) return true;
    o = Object.getPrototypeOf(o);
  }
  return false;
}
  // to the class. This symbol is unique per mixin definition, so a class will have N
  // applicationRefs if it has had N mixins applied to it. A mixin will have
  // exactly one _cachedApplicationRef used to store its applications.
  let applicationRef = mixin[_cachedApplicationRef];
  if (!applicationRef) {
    applicationRef = mixin[_cachedApplicationRef] = Symbol(mixin.name);
  }
  // Look up an existing application of `mixin` to `c`, return it if found.
  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;
});

 * @param {MixinFunction} wrapper A function that wraps {@link mixin}
 * @return {MixinFunction} `wrapper`
 */
export const wrap = (mixin, wrapper) => {
  Object.setPrototypeOf(wrapper, mixin);
  if (!mixin[_wrappedMixin]) {
    mixin[_wrappedMixin] = mixin;
  }
  return wrapper;
};
 * @param {MixinFunction} wrapper A wrapped mixin produced by {@link wrap}
 * @return {MixinFunction} The originally wrapped mixin
 */
export const unwrap = (wrapper) => wrapper[_wrappedMixin] || wrapper;

const _cachedApplications = '__mixwith_cachedApplications';

/**
 * Decorates `mixin` so that it caches its applications. When applied multiple
 * times to the same superclass, `mixin` will only create one subclass, memoize
 * it and return it for each application.
 *
 * Note: If `mixin` somehow stores properties its classes constructor (static
 * properties), or on its classes prototype, it will be shared across all
 * applications of `mixin` to a super class. It's reccomended that `mixin` only
 * access instance state.
 *
 * @function
 * @param {MixinFunction} mixin The mixin to wrap with caching behavior
 * @return {MixinFunction} a new mixin function
 */
export const Cached = (mixin) => wrap(mixin, (superclass) => {
  // Get or create a symbol used to look up a previous application of mixin
  // to the class. This symbol is unique per mixin definition, so a class will have N
  // applicationRefs if it has had N mixins applied to it. A mixin will have
  // exactly one _cachedApplicationRef used to store its applications.

  let cachedApplications = superclass[_cachedApplications];
  if (!cachedApplications) {
    cachedApplications = superclass[_cachedApplications] = new Map();
  }

  let application = cachedApplications.get(mixin);
  if (!application) {
    application = mixin(superclass);
    cachedApplications.set(mixin, application);
  }

  return application;
});

/**
 * Decorates `mixin` so that it only applies if it's not already on the
 * prototype chain.
 *
 * @function
 * @param {MixinFunction} mixin The mixin to wrap with deduplication behavior
 * @return {MixinFunction} a new mixin function
 */
export const DeDupe = (mixin) => wrap(mixin, (superclass) =>
    (hasMixin(superclass.prototype, mixin))
      ? superclass
      : mixin(superclass));

/**
 * Adds [Symbol.hasInstance] (ES2015 custom instanceof support) to `mixin`.
 *
 * @function
 * @param {MixinFunction} mixin The mixin to add [Symbol.hasInstance] to
 * @return {MixinFunction} the given mixin function
 */
export const HasInstance = (mixin) => {
  if (Symbol && Symbol.hasInstance && !mixin[Symbol.hasInstance]) {
    Object.defineProperty(mixin, Symbol.hasInstance, {
      value(o) {
        return hasMixin(o, mixin);
      },
    });
  }
  return mixin;
};

/**
 * A basic mixin decorator that applies the mixin with {@link apply} so that it
 * can be used with {@link isApplicationOf}, {@link hasMixin} and the other
 * mixin decorator functions.
 *
 * @function
 * @param {MixinFunction} mixin The mixin to wrap
 * @return {MixinFunction} a new mixin function
 */
export const BareMixin = (mixin) => wrap(mixin, (s) => apply(s, mixin));

/**
 * Decorates a mixin function to add deduplication, application caching and
 * instanceof support.
 *
 * @function
 * @param {MixinFunction} mixin The mixin to wrap
 * @return {MixinFunction} a new mixin function
 */
export const Mixin = (mixin) => DeDupe(Cached(BareMixin(mixin)));

/**
 * A fluent interface to apply a list of mixins to a superclass.
 *
 * ```javascript
 * class X extends mix(Object).with(A, B, C) {}
 * ```
 *
 * The mixins are applied in order to the superclass, so the prototype chain
 * will be: X->C'->B'->A'->Object.
 *
 * This is purely a convenience function. The above example is equivalent to:
 *
 * ```javascript
 * class X extends C(B(A(Object))) {}
 * ```
 *
 * @function
 * @param {Function} [superclass=Object]
 * @return {MixinBuilder}
 */
export const mix = (superclass) => new MixinBuilder(superclass);

class MixinBuilder {

  constructor(superclass) {
    this.superclass = superclass || class {};
  }

  /**
   * Applies `mixins` in order to the superclass given to `mix()`.
   *
   * @param {Array.<Mixin>} mixins
   * @return {Function} a subclass of `superclass` with `mixins` applied
   */
  with(...mixins) {
    return mixins.reduce((c, m) => m(c), this.superclass);
  }
}

<MSG> Remove need for Symbols.
<DFF> @@ -1,8 +1,7 @@
 'use strict';
 
-export const _cachedApplicationRef = Symbol('_cachedApplicationRef');
-export const _mixinRef = Symbol('_mixinRef');
-export const _originalMixin = Symbol('_originalMixin');
+// used by apply() and isApplicationOf()
+const _appliedMixin = '__mixwith_appliedMixin';
 
 /**
  * Applies `mixin` to `superclass`.
@@ -12,7 +11,7 @@ export const _originalMixin = Symbol('_originalMixin');
  */
 export const apply = (superclass, mixin) => {
   let application = mixin(superclass);
-  application.prototype[_mixinRef] = unwrap(mixin);
+  application.prototype[_appliedMixin] = unwrap(mixin);
   return application;
 };
 
@@ -24,7 +23,10 @@ export const apply = (superclass, mixin) => {
  * as created by `apply`.
  */
 export const isApplicationOf = (proto, mixin) =>
-  proto.hasOwnProperty(_mixinRef) && proto[_mixinRef] === unwrap(mixin);
+  proto.hasOwnProperty(_appliedMixin) && proto[_appliedMixin] === unwrap(mixin);
+
+// used by wrap() and unwrap()
+const _wrappedMixin = '__mixwith_wrappedMixin';
 
 /**
  * Sets up the function `mixin` to be wrapped by the function `wrapper`, while
@@ -39,8 +41,8 @@ export const isApplicationOf = (proto, mixin) =>
  */
 export const wrap = (mixin, wrapper) => {
   Object.setPrototypeOf(wrapper, mixin);
-  if (!mixin[_originalMixin]) {
-    mixin[_originalMixin] = mixin;
+  if (!mixin[_wrappedMixin]) {
+    mixin[_wrappedMixin] = mixin;
   }
   return wrapper;
 };
@@ -50,7 +52,7 @@ export const wrap = (mixin, wrapper) => {
  * one or more calls to `wrap`. Returns `wrapper` if it's not a wrapped
  * function.
  */
-export const unwrap = (wrapper) => wrapper[_originalMixin] || wrapper;
+export const unwrap = (wrapper) => wrapper[_wrappedMixin] || wrapper;
 
 
 /**
@@ -65,6 +67,8 @@ export const hasMixin = (o, mixin) => {
   return false;
 }
 
+const _cachedApplications = '__mixwith_cachedApplications';
+
 /**
  * Decorates `mixin` so that it caches its applications. When applied multiple
  * times to the same superclass, `mixin` will only create one subclass, memoize
@@ -80,18 +84,18 @@ export const Cached = (mixin) => wrap(mixin, (superclass) => {
   // to the class. This symbol is unique per mixin definition, so a class will have N
   // applicationRefs if it has had N mixins applied to it. A mixin will have
   // exactly one _cachedApplicationRef used to store its applications.
-  let applicationRef = mixin[_cachedApplicationRef];
-  if (!applicationRef) {
-    applicationRef = mixin[_cachedApplicationRef] = Symbol(mixin.name);
+
+  let cachedApplications = superclass[_cachedApplications];
+  if (!cachedApplications) {
+    cachedApplications = superclass[_cachedApplications] = new Map();
   }
-  // Look up an existing application of `mixin` to `c`, return it if found.
-  if (superclass.hasOwnProperty(applicationRef)) {
-    return superclass[applicationRef];
+
+  let application = cachedApplications.get(mixin);
+  if (!application) {
+    application = mixin(superclass);
+    cachedApplications.set(mixin, application);
   }
-  // Apply the mixin
-  let application = mixin(superclass);
-  // Cache the mixin application on the superclass
-  superclass[applicationRef] = application;
+
   return application;
 });
 
@@ -108,7 +112,7 @@ export const DeDupe = (mixin) => wrap(mixin, (superclass) => {
  * Adds [@@hasInstance] (ES2015 instanceof support) to `mixin`.
  */
 export const HasInstance = (mixin) => {
-  if (Symbol.hasInstance && !mixin[Symbol.hasInstance]) {
+  if (Symbol && Symbol.hasInstance && !mixin[Symbol.hasInstance]) {
     Object.defineProperty(mixin, Symbol.hasInstance, {
       value(o) {
         return hasMixin(o, mixin);

Remove need for Symbols.