窗口上的代理

时间:2017-08-01 12:18:12

标签: javascript ecmascript-6

我想设置一个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"

有没有办法在Proxy对象上设置window,如果不可能,是否有任何棘手的解决方案可以实现相同的目标?

4 个答案:

答案 0 :(得分:5)

简短的回答是否定的。您无法使用代理。 修改和重构你的应用程序总是更好,以摆脱这种恶作剧的需要。但我知道有时我们没时间做正确的事。通过我不建议你这样做,你仍然可以对窗口对象进行更改。

您有几个选项可以做到这一点。 如果您知道自己要查找的变量列表,则可以使用类似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

请记住在Watch.JS的情况下使用unwatch或添加条件以便在您完成作业后使用第二个解决方案时停止检查

答案 1 :(得分:3)

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

window属性为read-only,即它不可配置且没有设置访问者,不能用代理替换。 < / p>

可以监视Proxy更改的window的替代方法是Firefox特定的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);

答案 2 :(得分:1)

您实际上并没有尝试触发窗口代理。你需要这样做:

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

不,你不能

window = new Proxy(window, handler);

因为窗口是不可替代的。

答案 3 :(得分:0)

是的窗口是不可替换的,但是您可以使用伪造的近似窗口包装测试的代码:

`let handler = {
   defineProperty(target, key, descriptor) {
     console.log('hey', key);
     return false;
   }
 };

 let proxyWindow = new Proxy(window, handler);

 (function(window){
   
 //js
 
     window.foo = 'bar';
   
 //

 }.bind(proxyWindow,proxyWindow).call(proxyWindow));


 

` 如果经过测试的代码不太复杂,则可以,因为可能会泄漏到实际的窗口对象(例如setTimeout等),但也可以对其进行修补。