Обнаружение, когда переменная изменяется в JavaScript - PullRequest
2 голосов
/ 12 апреля 2019

У меня есть приложение Chrome, которое я хочу правильно изменить размер (размеры, пропорциональные ширине экрана) при изменении разрешения. Я написал функцию, которая перерисовывает приложение с правильными размерами, теперь я хочу выполнять его только тогда, когда это необходимо.

Изменение разрешения приводит к изменению screen.width, но (что неудивительно, поскольку они относятся к разным вещам) событие «resize» не вызывается, и, насколько я могу судить, событие не запускается.

Я знаю об объекте Proxy (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), поэтому я написал некоторый код, который обнаруживает, когда переменная установлена, и выполняет мой обратный вызов, похоже, это работает, но не в случае изменения разрешения.

Таким образом, я искал в Интернете и пробовал это https://gist.github.com/eligrey/384583 (что по сути является ответом, предоставленным в нескольких вопросах stackoverflow по аналогичной теме, и действительно тем, что я изначально создал, хотя в нем отсутствовала абстракция, которую предлагает новый объект Proxy) .

Это также, кажется, работает (в том смысле, если я скажу вручную установить screen.width после выполнения screen.watch ("width", () => console.log ("hello")); а затем сделать screen. ширина = 100; мой обратный вызов выполнен). Но не в случае изменения разрешения (на самом деле, возможно, самое главное, назначение этого наблюдателя, по-видимому, предотвращает присвоение screen.width).

У меня три вопроса

1) Что происходит, когда я назначаю наблюдателя / прокси, который все портит.

2) Как я могу узнать, что делает браузер на этом уровне детализации (что отправляет триггер для изменения screen.width? Я думаю, это ОС, как это выглядит)

3) Есть ли лучший способ добиться того, к чему я изначально стремился (изменение размера приложения chrome).

В основном меня интересует вопрос 1), и меня больше не волнует 3).

Чтобы повторить мою проблему,

  • открыть новую вкладку в Firefox или Chrome
  • перейти на консоль разработчика.
  • проверьте screen.width, измените разрешение, обратите внимание, что screen.width изменяется
  • Скопируйте и вставьте код из https://gist.github.com/eligrey/384583
  • Do screen.watch ("width", (id, oldval, newval) => {console.log ("hello"); вернуть newval;});
  • Do screen.width = 100; и обратите внимание, что привет зарегистрирован и screen.width установлен на 100
  • Измените разрешение, обратите внимание, что ширина экрана не установлена.

Edit - Как выяснилось после ответа Бертрана, событие изменения размера может фактически срабатывать при изменении разрешения, но это, похоже, является ответом на изменение разрешения, заставляющим границу окна уменьшаться, если окно маленькое, то screen.width может изменяться без запуска события изменения размера.

Ответы [ 2 ]

3 голосов
/ 15 апреля 2019

Что происходит, когда я назначаю наблюдателя / прокси, который все портит.

Проблема в том, что свойство width на самом деле является getter в Screen.prototype. Это не обычное значение для 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, потому что вы сможете перехватить присвоение свойству .width. Вот почему вы видите это после назначения своего собственного установщика на window.screen.width:

Выполните screen.width = 100; и обратите внимание, что привет зарегистрирован, и screen.width установлен на 100

Показывает hello - вы вызываете установщик, который ранее был назначен свойству screen window. Напротив, поскольку встроенное встроенное свойство не является простым значением, которое присваивается браузером, ваш 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);

Как видите, функция установки, добавленная к obj во внешней области видимости, не может зафиксировать изменение в privateVal. Это не точно , что происходит с window.screen, но это похоже; у вас нет доступа к базовой структуре кода, которая приводит к изменению значения, возвращаемого при вызове встроенного метода получения.


Встроенная функция получения в screen состоит из собственного кода - это означает, что это не обычная функция Javascript. Функции, составленные из собственного кода, всегда являются функциями, предоставляемыми браузером (или любой средой, в которой выполняется код); они часто не могут быть эмулированы никакими функциями Javascript, которые вы пишете сами. Например, only объект window.history может выполнять исторические действия, такие как history.back(); если window.history перезаписывается, и у вас нет сохраненной ссылки на него или любые другие объекты history, нет способа написать собственную функцию, которая может выполнять history.back(), потому что .back() вызывает привилегированный нативный код , который требует интерфейса между видимым Javascript и механизмом браузера.

Аналогично, встроенный window.screen геттер, при вызове, возвращает значение , не наблюдаемое напрямую простым Javascript. Без опроса единственный другой способ следить за изменениями - прослушивать событие resize, как описано в другом ответе. (Это resize событие, аналогичное базовому значению, возвращаемому window.screen, управляется внутренними элементами браузера , иначе не наблюдается.)

Если событие resize не запускается в ответ на изменение разрешения - например, если окно браузера уже достаточно мало, чтобы изменение не требовалось для меньшего разрешения, - изменение разрешения не приводит в любом случае увольнение означает, что, к сожалению, нет способа его прослушать, кроме опроса. ( Вы можете попробовать это сами , не похоже, что другие события запускаются при изменении разрешения)

Возможно, можно написать (или настроить) движок браузера, который прослушивает изменение разрешения ОС и отправляет событие Javascript при обнаружении, но для этого потребуются знания далеко за пределами Javascript - не стесняйтесь просматривать Источник Chromium код для деталей, но он довольно сложный, и, вероятно, непрозрачный, без опыта в используемых языках.

2 голосов
/ 12 апреля 2019

Согласно Mozilla событие изменения размера экрана вызывается только для объекта window. Поэтому вы должны добавить слушателя к window, а не к screen:

window.addEventListener('resize', yourfunc)

Протестировано под Windows 10, работает как шарм с Chrome. Стоит сказать, что фактический размер экрана, рассчитанный браузером, использует либо разрешение экрана , либо увеличение.

Например, если вы используете разрешение 1920x1080, увеличьте масштаб на 100%:

  • При переходе к 3840x2160, при увеличении 200% окно браузера будет отображаться с разрешением 1920x1080, а resize, похоже, не сработало
  • При переходе на увеличение 150% будет выведено окно браузера с разрешением 1280x720, и resize сработает

Тем не менее, есть ловушка. Обновление разрешения экрана будет обнаружено только в том случае, если оно фактически вызывает изменение размера окна браузера. Это не так очевидно, учитывая начальное состояние окна браузера в оконном / полноэкранном режиме и эволюцию меньшего / большего размера экрана.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...