Будь-яка вигода від “блокування” об’єктів JavaScript?


76

JavaScript 1.8.5 (ECMAScript 5) додає кілька цікавих методів, що запобігають майбутнім модифікаціям переданого об’єкта з різним ступенем ретельності:

Імовірно, головним пунктом їх є виявлення помилок: якщо ви знаєте, що не хочете модифікувати об'єкт після певної точки, ви можете заблокувати його, щоб помилка видала помилку, якщо ви ненавмисно спробуєте змінити її пізніше. (За умови, що ви це зробили "use strict";.)

Моє питання: у сучасних двигунах JS, таких як V8, чи є якісь переваги в продуктивності (наприклад, швидший пошук властивостей, зменшення обсягу пам’яті) при блокуванні об’єктів за допомогою вищезазначених методів?

(Див. Також приємне пояснення Джона Резіга - однак, не згадується про продуктивність.)


Відповіді:


82

У різниці в продуктивності не було різниці, оскільки принаймні Chrome 47.0.2526.80 (64-розрядна).

Testing in Chrome 6.0.3359 on Mac OS 10.13.4
-----------------------------------------------
Test               Ops/sec
non-frozen object  106,825,468  ±1.08%  fastest
frozen object      106,176,323  ±1.04%  fastest

Тест продуктивності (доступний за адресою http://jsperf.com/performance-frozen-object ):

  const o1 = {a: 1};
  const o2 = {a: 1};

  Object.freeze(o2);

  // Non-frozen object:
  for(var key in o1);

  // Frozen object:
  for(var key in o2);

Оновлення 30.10.2019 : Немає різниці в продуктивності Chrome 78.0.3904 (64-розрядна версія )

Оновлення 17.09.2019 : Немає різниці в продуктивності Chrome 76.0.3809 (64-розрядна версія )

Оновлення 03.05.2018 : Немає різниці в продуктивності Chrome 66.0.3359 (64-розрядна версія )

Оновлення 06.03.2017 : Немає різниці в продуктивності Chrome 56.0.2924 (64-розрядна версія )

Оновлення 13.12.2015 : Немає різниці в продуктивності Chrome 47.0.2526.80 (64-розрядна версія )


У Chrome 34 заморожений об'єкт працює трохи краще, ніж незаморожений у тесті @ pimvdb (результати нижче). Однак різниця, здається, недостатньо велика, щоб виправдати використання цієї техніки для підвищення продуктивності.

http://jsperf.com/performance-frozen-object

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  105,250,353  ±0.41%  3% slower
frozen object      108,188,527  ±0.55%  fastest

Запуск тестових випадків @ kangax показує, що обидві версії об'єкта виконують майже однаково:

http://jsperf.com/performance-frozen-object-prop-access

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  832,133,923  ±0.26%  fastest
frozen object      832,501,726  ±0.28%  fastest

http://jsperf.com/http-jsperf-com-performance-frozen-object-instanceof

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  378,464,917  ±0.42%  fastest
frozen object      378,705,082  ±0.24%  fastest

ваша відповідь хороша, я поставив +1, але вам слід було відредагувати застарілу відповідь, щоб зробити все правильно.
Nicocube

Дякуємо за ваш відгук, @Nicocube. Я не був впевнений, чи краще відредагувати застарілу відповідь чи написати нову. Я бачив, як обидва підходи використовуються для stackoverflow, але ваша пропозиція має сенс.
Ян Молак

Ваші посилання на jsperf порушені something went wrong, чи є у вас копія коду, який ви перевірили для своєї претензії?
Ferrybig

Схоже на проблему з jsperf, оскільки жодні інші посилання на них також не працюють ... Я спробую опублікувати зразок тут, коли вони знову в мережі.
Ян Молак

2
У мене був випадок, коли виклик Object.freeze () сам по собі був негативним для продуктивності, оскільки у мене було багато різних маленьких об'єктів (думаю, Вузли для великого дерева). Конструкція виявилася занадто важкою, тому я скинув Object.freeze ().
просто

14

Оновлення: Оскільки ця відповідь була написана спочатку, виправлена ​​помилка у V8, яка спричинила цю проблему . Докладніше див. У відповіді Яна Молака .


У Google Chrome (тобто V8, тобто) заморожений об'єкт ітерації на 98% повільніше, ніж звичайний об'єкт.

http://jsperf.com/performance-frozen-object

Test name*              ops/sec

non-frozen object    32,193,471
frozen object           592,726

Можливо, це тому, що ці функції відносно нові і, мабуть, ще не оптимізовані (але це лише моє припущення, я, чесно кажучи, не знаю причини).

У кожному разі, я справді не рекомендував використовувати його для підвищення продуктивності, оскільки це, мабуть, не має сенсу.


* Код тесту:

var o1 = {a: 1};
var o2 = {a: 1};

Object.freeze(o2);

Тест 1 (незамерзлий предмет):

for(var key in o1);

Тест 2 (заморожений предмет):

for(var key in o2);

7
Це звучить як помилка V8, а не як щось не оптимізоване. Примітка. Object.keys працює лише на 72% повільніше
Raynos,

3
@Raynos: Гарна думка; також Object.keysне повинен бути повільнішим . Я погоджуюсь, що це більше схоже на помилку, оскільки обмерзання не повинно бути успіхом; швидше навпаки.
pimvdb

3
Цікава знахідка. Я перевірив інші браузери - нічні FF, WebKit, Opera, і жоден з них не має такого шаленого гальмування (див. Jsperf ). Безумовно, схожа на помилку. Я подав проблему в трекер V8 - code.google.com/p/v8/issues/detail?id=1858
kangax

20
Зараз (у Chrome 34 та в 2014 році) ітерація замороженого об’єкта здається ітерацією приблизно на 24 відсотки швидше.
msung

7
Зараз це застаріло, проти голосування.
Emil Eriksson

13

Теоретично заморожування об'єкта дозволяє зробити більш чіткі гарантії щодо форми об'єкта.

Це означає, що віртуальна машина може ущільнити обсяг пам'яті.

Це означає, що ВМ може оптимізувати пошук властивостей у ланцюжку прототипів.

Це означає, що будь-які посилання в режимі реального часу просто стали не активними, оскільки об’єкт більше не може змінюватися.

На практиці двигуни JavaScript ще не роблять цієї агресивної оптимізації.


1
На практиці в більшості двигунів мало що можна отримати з точки зору пам'яті для будь-якого даного об'єкта. Так само, пошук властивостей прототипів вже кешований (продуктивність більшості вбудованих систем була б жахливою, якби це не було).
gsnedders

Правильно. Ви повинні мати більше, ніж просто вбудований кеш, оскільки ви можете вбудувати все прочитане, оскільки у вас є відоме значення.
gsnedders

(Зверніть увагу, що вбудовування отримає лише певну кількість: наприклад, ви не хочете вбудовувати рядки, хоча вбудовані цілі числа / подвійні - це те, що ви хочете зробити.)
gsnedders

@Raynos, на жаль, ми все ще можемо змінити прототип об'єкта навіть після того, як його заморожено. Одним із способів отримати дійсно стабільну форму є встановлення прототипу null. Інший - заморозити весь ланцюжок прототипів разом із Object.prototype(звучить страшно).
tomekwi

"Це означає, що будь-які посилання в режимі реального часу просто не стали активними, оскільки об'єкт більше не може змінюватися" - я думаю, це нерозуміння того, що означає посилання бути активним . Посилання, яке не можна змінити, все ще може бути активним.
davmac


2

Якщо ви зацікавлені в продуктивності створення об’єкта (буквальний проти замороженого проти запечатаного проти Immutable.Map), я створив тест на jsPerf щоб перевірити це.

Наразі я мав можливість протестувати його лише в Chrome 41 і Firefox 37. В обох браузерах створення замороженого або запечатаного об’єкта займає приблизно втричі більше часу, ніж створення літералу - тоді як ефективність Immutable.Mapвиконується приблизно в 50 разів гірше, ніж буквальний.


FWIW, покарання в 3 рази краще, ніж я очікував. Я із задоволенням буду використовувати заморожені предмети, за винятком критичних ситуацій.
tomekwi

Дуже погано, якщо це займає 3 рази більше часу ... згідно зв'язаного jsperf, ця помилка продуктивності все ще присутня (Chrome 49). Object.freeze з літералом у ньому повинен бути виявлений компілятором як намір заморозити об’єкт. Тобто це не повинні бути два окремі кроки, один - створення об’єкта, а другий - заморожування. Створення (і доступ до нього) замороженого об’єкта має бути однаковим або швидшим. Схоже, гідний звіту про випуск.
Роберт Монфера,

Я можу підтвердити. Впроваджував незмінні структури даних у JavaScript, заморожування внутрішніх вузлів значно уповільнює процес. Це поганий вибір, коли всюди створюється багато нових об’єктів. Копіювання всього шляху, яке ви робите з незмінними структурами даних, у підсумку надто багато. На мою думку, це приблизно 2x-3x, але більше 2x коефіцієнт, ніж 3x коефіцієнт.
Джон Лейдегрен

-1

Єдина причина, яку я бачу для цих методів у виробничому коді, полягає в тому, що ви можете запечатати або заморозити предмети для цілісності.

Наприклад, я пишу невеличку бібліотеку, яка чудово працює і пропонує вам набір методів в об'єкті, але я не хочу, щоб ви змінювали або перезаписували будь-які мої властивості чи методи. Я не кажу, що можу перешкодити вам зробити це, але я можу спробувати перешкодити вам зробити це випадково, що, можливо, важливіше.

Крім того, ці методи легко «обшити» в середовищі, яке про них не знає, просто повернувши оригінальний об’єкт. Звичайно, тоді це не мало би ефекту.

Я не бачу жодних причин, пов’язаних з продуктивністю, щоб це зробити.


1
цілі цілісності призначені для розвитку
Райнос,

@Raynos: Я все одно бачу призначення бібліотек. Як я вже говорив, більше шансів захистити цілісність об'єктів від небажаних змін.
jAndy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.