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.methodshould always be trueobj.methodinherited 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.