Is it possible, given the following object
let target = {
foo:0,
result:[],
bar(){
//some code
}
}
to then wrap said object in a Proxy()
let handler = {
get(){
// code here
},
apply(){
// code here
}
}
target = new Proxy(target,handler);
that catch the call to bar()
and save the result to results:[]
?
I've given it a few tries
let target = {
called:0,
results:[],
foo(bar){
return bar;
},
}
let handler = {
get(target,prop){
console.log('Get Ran')
return target[prop];
},
apply(target,thisArg,args){
console.log('Apply Ran')
// never runs
}
}
target = new Proxy(target,handler);
target.foo();
This code misses [[ apply ]] but catches the [[ get ]] (if memory serves, object method calls are done as two operations , [[ get ]] [[ apply ]])
let target = {
called:0,
results:[],
foo(bar){
return bar;
},
}
let handler = {
get(target,prop){
return target[prop];
},
apply(target,thisArg,args){
let product = target.apply(thisArg,args)
return product;
},
}
let prox = new Proxy(target.foo,handler);
console.log(prox('hello'));
If I instead wrap the object method in a proxy it catches the [[ apply ]] but I lose the reference to the original object ( this ) and therefore lose access to the result array
I've also tried nesting the method proxy inside of the object proxy.
any thoughts ?
// Documentation for Proxy
// Other questions , about proxy , but not this use case
According to spec
A Proxy exotic object only has a [[Call]] internal method if the initial value of its [[ProxyTarget]] internal slot is an object that has a [[Call]] internal method.
So below does catch the invocation if target is a function
(new Proxy(() => 'hello', { apply: () => console.log('catched') }))() // 'catched
Reciprocally, if target does not have a call method, then no proxying
try {
(new Proxy({}, { apply: () => console.log('catched') }))() // throws
} catch (e){ console.log('e', e.message)}
So hint may be to proxy [get] then instead of returning the value (being the function bar
, proxy that value to trap the eventual invocation
const p = new Proxy(
{ results: [], bar: () => { console.log('rywhite') } }, {
get: (target, prop) => {
if (prop !== 'bar') return target[prop]
return new Proxy (target.bar, {
apply () {
target.results.push('what')
}
})
}
})
p.bar // nothing bud'
p.bar(); console.log('res', p.results)
p.bar(); console.log('res', p.results)
p.bar(); console.log('res', p.results)
edit: NB: it is not necessary to create a new proxy every time
In code below, returning the same proxy is about twice faster
const N = 1e6
{
const target = { results: 0, bar: () => { console.log('rywhite') } }
const p = new Proxy(
target, {
get: (() => {
const barProxy = new Proxy (target.bar, {
apply () {
target.results++
}
})
return (target, prop) => {
if (prop !== 'bar') return target[prop]
return barProxy
}
})()
})
console.time('go')
for (let i = 0; i < N; ++i) { p.bar() }
console.timeEnd('go')
console.log('res', p.results)
}
{
const p = new Proxy(
{ results: 0, bar: () => { console.log('rywhite') } }, {
get: (target, prop) => {
if (prop !== 'bar') return target[prop]
return new Proxy (target.bar, {
apply () {
target.results++
}
})
}
})
console.time('neweverytime')
for (let i = 0; i < N; ++i) { p.bar() }
console.timeEnd('neweverytime')
console.log('res', p.results)
}