代理窗口

问题描述:

我想设置一个Proxy,它在window对象上定义一个新属性时会发出警告。 (其实我想抓住所有的全局变量声明)代理窗口

let handler = { 
    defineProperty(target, key, descriptor) { 
     console.log('hey', key); 
     return false; 
    } 
}; 
window = new Proxy(window, handler); 
window.foo = 'bar'; 
// nothing happens 

上面的代码适用于任何对象,但窗口:

let handler = { 
    defineProperty(target, key, descriptor) { 
     console.log('hey', key); 
     return false; 
    } 
}; 
let target = {}; 
target = new Proxy(target, handler); 
target.foo = 'bar'; 
// console: "hey bar" 

有什么办法来建立一个Proxywindow对象,如果不可能,是否有任何棘手的解决方案来实现相同的目标?

+3

不行,因为你不能使用你的代理替换窗口。 – Bergi

+2

您的代码只应记录'proxy.foo ='bar'',而不是'target.foo ='bar''。你发布的内容是否真的有用? – Bergi

+2

可能的重复[如何检测当一个全局变量设置为JavaScript?](https://stackoverflow.com/q/38759116/1048572) – Bergi

简短的回答是没有。你不能使用这个代理。 修改并重构您的应用程序以摆脱需要执行此类恶作剧总是更好。但我知道有时我们没有时间做正确的事情。虽然我不建议你这样做,但你仍然可以对窗口对象进行更改。

你有几个选择来做到这一点。 如果你知道你正在寻找的变量列表,你可以使用类似Watch.JS的东西基本上它能够跟踪所有的变化,但我无法使它可靠地工作,所以最好指定一个列表

watch(window, ['list', 'of', 'vars'], (prop, action, newVal, oldVal) => { 
    console.log('Property changed', prop, action, newVal, oldVal); 
}, 1); 

作为替代方案,您可以创建一个简单的脏检查

let props = Object.keys(window); 
const check =() => { 
    const currentProps = Object.keys(window); 
    const newProps = currentProps.filter(item => props.indexOf(item) === -1); 
    if (newProps.length) { 
     console.log('Added these properties', newProps); 
     props = currentProps; 
    } 
    requestAnimationFrame(check); 
}; 
requestAnimationFrame(check); 

,但如果你决定去与任何一个解决方案,你必须确保在需要的时候,以避免内存泄漏所有的检查将停止或CPU消耗。 这个检查代码并没有消耗太多,但理论上可以。所以你必须留意它。 在空白页面的个人资料数据看起来像这样profile data

记住使用unwatch在Watch.JS或情况下添加一个条件停止的情况下,你使用第二个解决方案中的检查,一旦他们将完成这项工作

+0

请注意,不能使用Object.keys监视不可枚举的道具。如果观看不可枚举的道具,演出将会更加糟糕。 – estus

+0

@estus这个解决方案已经足够了,我还没有使用'getOwnPropertyNames'来减少集合大小来过滤。由于作者只想获取全局变量声明,而不是观察所有窗口属性。 –

+0

窗口属性也可以用Object.defineProperty声明。当预先知道这些变量是什么时,任务变得更直接。 – estus

你实际上没有试图触发窗口代理。你需要做的:

let proxy = new Proxy(window, handler); 
proxy.foo = 'bar'; 

不,你不能做

window = new Proxy(window, handler); 

因为窗口是不可替代的。

Proxy速度很慢,不应在性能关键的地方使用,而window会影响整个应用程序,当然也可能会被视为性能至关重要。

window属性是read-only,即它是不可配置的,并且没有set访问器,它不能被替换为代理。

的替代Proxy可以在window变化窥探是火狐特异性watch方法,它可以在在Firefox(例如扩展),而不是任何其他地方运行脚本中使用。 V8特定的Object.observe无法通过设计观察到window,而且它已从Chrome和其他V8浏览器中删除。

通常,这可以通过轮询window属性来实现:

let oldProps; 

setInterval(() => { 
    console.time('Polling window'); 
    let newProps = Object.getOwnPropertyNames(window); 

    if (oldProps) { 
    let addedProps = newProps.filter(prop => oldProps.indexOf(prop) < 0); 
    console.log('Added props', addedProps); 
    } 
    oldProps = newProps; 
    console.timeEnd('Polling window'); 
}, 500); 

如果该代码应该在生产中使用,它应该被优化,因为filter相对较慢,并indexOf遍历整个阵列上每次迭代都会导致非常低效的代码。

forwhile环是要走的路:

let oldProps; 

setInterval(() => { 
    console.time('Polling window'); 
    let newProps = Object.getOwnPropertyNames(window).sort(); 

    if (oldProps) { 
    for (let oldI = 0, newI = 0; oldI < oldProps.length || newI < newProps.length; oldI++, newI++) { 
     let oldProp = oldProps[oldI]; 
     let newProp = newProps[newI]; 

     if (newProp > oldProp || newProp === undefined) { 
     newI--; 
     console.log('Removed prop', oldProp); 
     } else if (newProp < oldProp || oldProp === undefined) { 
     oldI--; 
     console.log('Added prop', newProp); 
     } 
    } 
    } 
    oldProps = newProps; 
    console.timeEnd('Polling window'); 
}, 500);