src/binder_mixin.js

import { isArray, range, reduce } from 'underscore';

/**
 * @mixin
 * @name BinderMixin
 */
export default {
  /**
   * @function
   * @name BinderMixin#$
   * @param {string} method  Name of method to bind
   * @returns {Function} Method named by *method* bound to the Object on which this method is called
   *
   * @description
   * Because lens access can easily traverse multiple layers — especially in
   * the context of a [datum plan]{@link module:natural-lenses/datum-plan} —
   * using `Function.prototype.bind` can involve significant repetition.  This method allows
   * such binding *without* the repetition, as the method name passed is
   * looked up against the target of the {@link Lens} (or other optic) and
   * then `Function.prototype.bind`ed to that Object.
   *
   * This method is also invokable via ES6 tagged template literal syntax.
   */
  $(method) {
    // Support tagged template syntax
    if (isArray(method)) {
      method = reduce(
        range(1, arguments.length),
        (cur, i) => cur + arguments[i].toString() + method[i],
        method[0]
      );
    }
    return this[method].bind(this);
  }
};