Reflect.set(...args, receiver) throwing for no reason

I swear I read binding before and because that’s a delicate topic I’d like to explain to future readers how not to bind methods because:

  • obj.method === obj.method should always be true
  • obj.method inherited from a prototype should not bloat RAM by creating, per each object, a bound version of that inherited method
const { apply, get, set } = Reflect;

const methods = new WeakMap;
const proxies = new WeakMap;

const methodHandler = {
  apply: (target, self, args) => apply(
    target,
    // proxy as context -> real target
    proxies.get(self) ?? self,
    args,
  ),
};

const sourceHandler = {
  get(target, key, proxy) {
    let value = get(target, key);
    if (typeof value === 'function') {
      if (!proxies.has(proxy)) {
        // remember the real target
        proxies.set(proxy, target);
      }
      if (!methods.has(value)) {
        // wrap this function as a proxy *once*
        methods.set(value, new Proxy(value, methodHandler));
      }
      value = methods.get(value);
    }
    return value;
  },
  set: (target, key, value) => set(target, key, value),
};

const wrap = value => new Proxy(value, sourceHandler);

// example

wrap(document.body).append('OK');

edit

as any context could be also a primitive, a guard around (typeof self === ‘object‘ && self) || typeof object === ‘function’ might be needed before passing proxies.get(self) ?? self or just self instead, for more convoluted / unknown scenarios.