Source: curried-demethodify.js

/**
 * Takes a `method` as input and transforms it into a curried function taking an instance
 * as its last parameter.
 *
 * @function curriedDemethodify
 *
 * @see demethodify
 *
 * @example
 * const { curriedDemethodify } = require("helpbox");
 *
 * function Person(age) {
 *     this.age = age;
 * }
 *
 * Person.prototype.ageInNYears = function (n) {
 *     return n + this.age;
 * }
 *
 * const ageInNYears = curriedDemethodify(Person.prototype.ageInNYears);
 * const person      = new Person(3);
 *
 * const ageIn10Years = ageInNYears(10);
 *
 * // Prints 13.
 * console.log(ageIn10Years(person));
 *
 * @param methodName {String}   The name of the method on the prototype.
 * @param method     {Function} The method to demethodify. It must have no rest parameters
 *                              and all arguments must be named. The arguments cannot have
 *                              default values.
 *
 * @return {Function} The demethodified function
 */
module.exports = (methodName, method) => {
    const staticVersionArity = method.length + 1;

    const recurry = (...parameters) => {
        if (parameters.length < staticVersionArity) {
            return (...additionalParameters) => {
                return recurry(...parameters.concat(additionalParameters));
            };
        }

        const instance = parameters[staticVersionArity - 1];

        parameters.splice(staticVersionArity - 1, 1);

        return (instance[methodName] || method).call(instance, ...parameters);
    };

    return recurry;
};