有没有办法使用 Javascript ES6 Proxy 来监视对象方法
Is there a way to use Javascript ES6 Proxy to spy on Object Methods
是否可能,给定以下对象
let target = {
foo:0,
result:[],
bar(){
//some code
}
}
然后将所述对象包裹在 Proxy()
中
let handler = {
get(){
// code here
},
apply(){
// code here
}
}
target = new Proxy(target,handler);
捕获对 bar()
的调用并将结果保存到 results:[]
?
我试了几次
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();
这段代码错过了 [[ apply ]] 但抓住了 [[ get ]]
(如果没记错的话,对象方法调用是作为两个操作完成的,[[ 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'));
如果我改为将对象方法包装在代理中,它会捕获 [[ apply ]]
但我丢失了对原始对象 ( this ) 的引用,因此无法访问结果数组
我也试过将方法代理嵌套在对象代理中。
有什么想法吗?
// 代理文档
// 其他问题,关于代理,但不是这个用例
根据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.
如果目标是 function
,那么下面确实会捕获调用
(new Proxy(() => 'hello', { apply: () => console.log('catched') }))() // 'catched
反过来,如果目标没有调用方法,则没有代理
try {
(new Proxy({}, { apply: () => console.log('catched') }))() // throws
} catch (e){ console.log('e', e.message)}
因此提示可能是代理 [get] 而不是返回值(作为函数 bar
,代理该值以捕获最终调用
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)
编辑:注意:没有必要每次都创建一个新代理
在下面的代码中,返回相同的代理大约快两倍
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)
}
是否可能,给定以下对象
let target = {
foo:0,
result:[],
bar(){
//some code
}
}
然后将所述对象包裹在 Proxy()
let handler = {
get(){
// code here
},
apply(){
// code here
}
}
target = new Proxy(target,handler);
捕获对 bar()
的调用并将结果保存到 results:[]
?
我试了几次
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();
这段代码错过了 [[ apply ]] 但抓住了 [[ get ]] (如果没记错的话,对象方法调用是作为两个操作完成的,[[ 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'));
如果我改为将对象方法包装在代理中,它会捕获 [[ apply ]] 但我丢失了对原始对象 ( this ) 的引用,因此无法访问结果数组
我也试过将方法代理嵌套在对象代理中。
有什么想法吗?
// 代理文档
// 其他问题,关于代理,但不是这个用例
根据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.
如果目标是 function
(new Proxy(() => 'hello', { apply: () => console.log('catched') }))() // 'catched
反过来,如果目标没有调用方法,则没有代理
try {
(new Proxy({}, { apply: () => console.log('catched') }))() // throws
} catch (e){ console.log('e', e.message)}
因此提示可能是代理 [get] 而不是返回值(作为函数 bar
,代理该值以捕获最终调用
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)
编辑:注意:没有必要每次都创建一个新代理
在下面的代码中,返回相同的代理大约快两倍
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)
}