最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Proxy on window - Stack Overflow

programmeradmin0浏览0评论

I would like to set up a Proxy which warns me when a new property is defined on the window object. (Actually I'd like to catch all the global variable declarations)

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

The code above works for any object but window:

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"

Is there any way to set up a Proxy on the window object, and if it's not possible, is there any tricky solution to achieve the same goal?

I would like to set up a Proxy which warns me when a new property is defined on the window object. (Actually I'd like to catch all the global variable declarations)

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

The code above works for any object but window:

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"

Is there any way to set up a Proxy on the window object, and if it's not possible, is there any tricky solution to achieve the same goal?

Share Improve this question edited Aug 3, 2017 at 13:06 Adam asked Aug 1, 2017 at 12:18 AdamAdam 5,2332 gold badges32 silver badges62 bronze badges 9
  • 3 No, since you cannot replace window with your proxy. – Bergi Commented Aug 1, 2017 at 12:20
  • 2 Your code should only log for proxy.foo = 'bar', not for target.foo = 'bar'. Does what you posted really work? – Bergi Commented Aug 1, 2017 at 12:21
  • 2 possible duplicate of How can I detect when a global variable is set in javascript? – Bergi Commented Aug 1, 2017 at 12:22
  • 1 @Rajesh Can't close when the target has no upvoted/accepted answers (and I can't upvote my own answer there :-D) – Bergi Commented Aug 1, 2017 at 12:25
  • 2 "warns me when a new property is defined on the window object." if want this feature to be development time I'd recommend you to use a linter for that. – Yury Tarabanko Commented Aug 3, 2017 at 12:31
 |  Show 4 more comments

4 Answers 4

Reset to default 6 +100

Short answer is no. You can't use a proxy for this. It is always better to revise and refactor your app to get rid of need to do such shenanigans. But I know that sometimes we have no time to do things right. Althrough I don't recommend you to do this, you still can get a changes on window object.

You have a couple of options to do this. If you know a list of vars you're looking for, you can use something like Watch.JS Basically it is able to track all the changes, but I wasn't able to make it work reliably so it is better to specify a list

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

As an alternative, you can create a simple dirty checker

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);

But in case you decided to go with either solution, you have to make sure all checks will stop when needed to avoid memory leaks, or CPU consumption. This check code is not consuming too much but it could in theory. So you have to keep an eye on it. On empty page profile data looks like this

And remember to use unwatch in case of Watch.JS or to add a condition to stop the checks in case you use the second solution once they will complete the job

Proxy is slow and shouldn't be used in performance-critical places, while window affects the entire application and certainly could be considered performance-critical.

But the window property is read-only, i.e. it is non-configurable and has no set accessor, it can't be replaced with a proxy.

The alternative to Proxy that can spy on window changes is Firefox-specific watch method, it can be used in scripts that run in Firefox (e.g. extensions) but not anywhere else. V8-specific Object.observe was unable to observe window by design, also, it was removed from Chrome and other V8 browsers.

Generally this can be achieved by polling window properties:

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);

If this code is supposed to be used in production, it should be optimized, because filter is relatively slow, and indexOf traverses the entire array on each iteration, this results in very inefficient code.

Raw for or while loop is the way to go:

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);

Youre actually not trying to trigger the window proxy. You need to do:

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

And no, you cant do

window = new Proxy(window, handler);

as window is unreplaceable.

Yes window is unreplaceable, but you can wrap the tested code with a fake proxified window:

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));

If the tested code is not too complicated, this will be OK, as leaks to the actual window object may occur (ex: setTimeout etc) but these can be patched too.

发布评论

评论列表(0)

  1. 暂无评论