检测变量何时在javascript中更改

时间:2019-04-12 16:09:21

标签: javascript google-chrome

我有一个Chrome应用,我想在分辨率更改时正确调整大小(尺寸与屏幕宽度成比例)。我编写了一个函数,可以用正确的尺寸重绘应用程序,现在我只想在需要时执行它。

分辨率的改变会导致screen.width发生变化,但是(因为它们与不同的事物有关,所以可能不足为奇)“调整大小”事件没有触发,据我所知没有触发任何事件。

我了解Proxy对象(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy),所以我写了一些代码来检测何时设置了变量并执行我的回调,这似乎起作用,但在分辨率更改的情况下不起作用。

因此,我在线搜索并尝试了https://gist.github.com/eligrey/384583(本质上,这是在一个类似主题的几个stackoverflow问题中提供的答案,实际上是我最初产生的结果,尽管缺少新的Proxy对象提供的抽象概念)。

这似乎也可行(从某种意义上说,如果我说在完成screen.watch(“ width”,()=> console.log(“ hello”))之后手动设置screen.width);然后再进行屏幕显示。 width = 100;我的回调已执行)。但不是在分辨率改变的情况下(实际上,也许最重要的是,分配此监视程序似乎阻止了screen.width的分配)。

我有三个问题

1)当我分配使事情混乱的观察者/代理时,会发生什么情况。

2)我如何才能在这种详细程度下了解浏览器的功能(是什么发送触发来更改screen.width的?我猜是操作系统,这是什么样的?)

3)是否有更好的方法来实现我最初想要的功能(调整chrome应用程序的大小)。

大多数情况下,我对问题1)感兴趣,并且不再关心3)。

要复制我的问题,

  • 在Firefox或chrome中打开新标签页
  • 转到开发者控制台。
  • 检查screen.width,更改分辨率,观察screen.width发生变化
  • https://gist.github.com/eligrey/384583复制代码并粘贴
  • 执行screen.watch(“ width”,(id,oldval,newval)=> {console.log(“ hello”); return newval;});
  • 执行screen.width = 100;并观察到您好已登录并且screen.width设置为100
  • 更改分辨率,观察未设置screen.width。

编辑-正如Bertrand回答后所揭示的,resize事件实际上可能会在分辨率更改时触发,但这似乎是对分辨率更改的响应,如果窗口很小,则迫使窗口的边界变小。 screen.width可以更改,而无需触发调整大小事件。

2 个答案:

答案 0 :(得分:3)

  

当我分配一个混乱的观察者/代理时发生了什么。

问题是width属性实际上是Screen.prototype上的 getter 。它不是window.screen上的普通值:

console.log(window.screen.width);
console.log(window.screen.hasOwnProperty('width'));

// The property exists on the prototype instead, and is a getter:
const descriptor = Object.getOwnPropertyDescriptor(Screen.prototype, 'width');
console.log(descriptor);

如果width属性是普通属性,仅由一个普通值组成,该值是浏览器通过eval ing设置的

window.screen.width = 1500

然后可以使用代理或Object.watch polyfill,因为您可以拦截对.width属性的分配。这就是为什么在将自己的设置器分配给window.screen.width之后看到的原因:

  

执行screen.width = 100;并观察到您好已记录,并且screen.width设置为100

它向您显示hello-您正在调用先前分配给screen的{​​{1}}属性的设置器。相反,由于本机内置属性不是浏览器分配给它的普通值,因此当屏幕变化时,window不会被记录。这种情况是 bit ,类似于本示例代码片段中的操作:

hello

如您所见,添加到外部作用域const obj = (() => { let privateVal = 'propOriginal'; // privateVal changes after 500ms: setTimeout(() => { console.log('Changing privateVal to propChanged'); privateVal = 'propChanged'; }, 500); // Return an object which has a getter, which returns privateProp: return { get privateProp() { return privateVal; } }; })(); // At this point, if one only has a reference to `obj`, // there is no real way to watch for changes to privateVal: console.log(obj.privateProp); // Assigning a custom setter to obj.privateProp // will only result in observing attempted assignments to obj.privateProp // but will not be able to observe the change to privateVal: Object.defineProperty(obj, 'privateProp', { set(newVal) { console.log('Observed change: new value is ' + newVal); }}); setTimeout(() => { obj.privateProp = 1000; }, 2500);中的setter函数无法捕获对obj的更改。 privateVal并非完全如此,但这是相似的。您无权访问底层代码结构,该代码结构会导致通过调用内置getter返回的值发生更改。


window.screen上内置的getter函数由本机代码组成-这意味着它不是普通的Javascript函数。由本机代码组成的功能始终是浏览器提供的功能(或运行代码的任何环境);您自己编写的任何Javascript函数通常都无法模仿它们。例如,window.history对象仅 可以执行诸如screen之类的历史记录操作;如果history.back()被覆盖,并且您没有保存对它或任何其他window.history对象的引用,则无法编写自己的函数来执行history,因为{{ 1}}调用特权本机代码,该代码需要在可见Javascript和浏览器引擎之间建立接口。

类似地,内置history.back()的getter在被调用时会返回纯Javascript无法直接观察的值。如果不进行轮询,监视更改的唯一其他方法是监听.back()事件,如其他答案所述。 (此window.screen事件类似于resize返回的基础值,由浏览器内部函数管理,否则无法观察。)

如果resize事件没有因分辨率的改变而被触发-例​​如,如果浏览器窗口已经足够小,则较小的分辨率不需要进行更改-则分辨率改变了不会导致任何事件被触发,这意味着除了轮询之外,没有其他方法可以监听它。 (You can try it for yourself,看起来好像没有其他事件是在分辨率更改时触发的)

一个人可能会编写(或调整)一个浏览器引擎,该监听器会侦听来自操作系统的分辨率变化并在发现时调度Javascript事件,但是这样做需要的知识远远超出Javascript-随时浏览Chromium's source code详细信息,但它相当复杂,对于没有使用过的语言经验的人来说可能是不透明的。

答案 1 :(得分:2)

According to Mozilla,仅在window对象上触发屏幕调整大小事件。因此,您应该将监听器添加到window而不是screen中:

window.addEventListener('resize', yourfunc)

在Windows 10上进行了测试,它在Chrome上的魅力十足。值得一提的是,浏览器计算出的实际屏幕尺寸使用的是屏幕分辨率

例如,如果您使用1920x1080,请缩放100%:

  • 传递到3840x2160时,缩放200%将输出1920x1080的浏览器窗口,并且resize似乎未触发
  • 传递150%的缩放比例将输出1280x720的浏览器窗口,并触发resize

尽管如此,还是有一个陷阱。仅当屏幕分辨率更新实际触发浏览器窗口大小调整时,才会检测到屏幕分辨率更新。考虑到浏览器窗口的窗口化/全屏初始状态以及屏幕尺寸的变小/变大,这并不是很明显。