Proxy working

by Pedro Moreira

HTML

https://jsfiddle.net/pedsmoreira/fz6d6p5j/48/#collaborate

JavaScript

function isFunction(value) {
  return typeof value === 'function';
}

function isFunctionWithSingleOrZeroArgs(value) {
  return isFunction(value) && value.length <= 1;
}

function proxyArrayFunctionCurry(arrayFunction, property) {
  const fn = function(...args) {
    const result = arrayFunction(function(item) {
      return item[property](...args);
    });

    return result;
  };

  return new Proxy(fn, {
    get: function(target, property) {
      return target()[property];
    }
  });
}

function proxyArrayFunction(array, fn) {
  const boundFn = fn.bind(array);
  return new Proxy(fn, {
    get: function(target, property) {
      if (property === '_fn') return fn;
      if (property === 'bind') return null;

      if (fn.length === 0) {
        const fnResult = boundFn();
        const value = fnResult[property];

        if (value.bind) {
          return value.bind(fnResult);
        }
        return value;
      }

      const reflector = (array[0] || {})[property];
      if (typeof reflector === 'function') {
        return proxyArrayFunctionCurry(boundFn, property);
      }

      return boundFn((item) => item[property]);
    },
    apply: function(target, thisArg, argumentsList) {
      if (isFunction(thisArg)) {
        thisArg = thisArg();
      }

      return target.bind(thisArg)(...argumentsList);
    }
  })
}

function jewell(target, method) {
  const fn = target[method];
  Object.defineProperty(target, method, {
    get: function() {
      return proxyArrayFunction(this, fn);
    }
  })
}

function jewellPrototype(target) {
  Object.getOwnPropertyNames(target.prototype).forEach((property) => {
    if (property === 'constructor') return;

    const value = target.prototype[property];
    if (isFunctionWithSingleOrZeroArgs(value)) {
      console.info(`${target.name}.${property} jewelled`)
      jewell(target.prototype, property);
    }
  })
}

class Animal {
  constructor(name, nickname) {
    this.name = name;
    this.nickname = nickname;
  }

 ...