Разница между заморозкой и печатью


164

Я только что услышал о методах JavaScript freezeи seal, которые можно использовать для того, чтобы сделать любой объект неизменным.

Вот короткий пример, как его использовать:

var o1 = {}, o2 = {};
Object.freeze(o2);

o1["a"] = "worked";
o2["a"] = "worked";

alert(o1["a"]);   //prints "worked"
alert(o2["a"]);   //prints "undefined"

В чем разница между freezeи seal? Могут ли они повысить производительность?


6
Просто примечание для всех, кто смотрит на этот вопрос, принятый ответ фактически неверен. Ответ @ tungd правильный.
Бьорн

2
Еще одно примечание, есть также Object.preventExtensionsв дополнение к Object.sealи Object.freeze. Object.preventExtensionsпросто предотвращает добавление новых предметов к объекту. Вы можете удалять, настраивать и изменять значения свойств объектов, у которых отключена расширяемость Object.preventExtensions.
Бьорн

1
Последнее обновление: v8.dev/blog/v8-release-76#frozen%2Fsealed-array-improvements
Майк

Ответы:


193

Object.seal

  • Предотвращает добавление и / или удаление свойств из запечатанного объекта; использование deleteвернет false
  • Это делает каждое существующее свойство неконфигурируемым : они не могут быть преобразованы из «дескрипторов данных» в «дескрипторы доступа» (и наоборот), и никакой атрибут дескрипторов доступа не может быть изменен вообще (тогда как дескрипторы данных могут изменить свой writableатрибут, и их valueатрибут, если writeableэто правда).
  • Может выдавать TypeErrorпри попытке изменить значение самого запечатанного объекта (чаще всего в строгом режиме )

Object.freeze

  • Что Object.sealконкретно, плюс:
  • Это предотвращает изменение любых существующих свойств

Ни один из них не влияет на «глубокие» / внуковые объекты. Например, если objон заморожен, obj.elего нельзя переназначить, но значение obj.elможно изменить, например, obj.el.idможно изменить.


Производительность:

Запечатывание или замораживание объекта может повлиять на скорость его перечисления, в зависимости от браузера:

  • Firefox: на производительность перечисления это не влияет
  • IE: влияние на производительность перечисления незначительно
  • Chrome: производительность перечисления быстрее с запечатанными или замороженными объектами
  • Safari: закрытые или замороженные объекты подсчитывают на 92% медленнее (по состоянию на 2014 год)

Тесты: Запечатанные объекты , Замороженные объекты .


2
Можете ли вы рассказать о том, почему мы будем использовать эти методы? Просто потому что мы можем?
Алан Донг

3
В будущем я думаю, что они будут много использоваться (если будут оптимизированы правильно) при разработке библиотеки / фреймворка. Они позволяют предотвращать (даже непреднамеренно) взломать ваш код пользователем (и, как указано в ответе, оптимизация должна привести к значительному улучшению скорости). Но это чистое предположение :)
Никколо Камполунго

2
Этот ответ имеет много фактических ошибок. С одной стороны, sealтакже делает существующие свойства неконфигурируемыми, см. Jsfiddle.net/btipling/6m743whn Номер 2, вы все еще можете редактировать, то есть изменять значения существующих свойств на запечатанном объекте.
Бьорн

8
FWIW, замороженные и запечатанные объекты теперь быстрее, чем их незамерзшие и распечатанные аналоги в Chrome Canary v43.0.2317.0.
Лямбда

2
@AlanDong Немного запоздала, но вот почему вы хотите заблокировать объект. Одной из особенностей JavaScript является то, что вы можете добавить свойство в любое время; Вы также можете сделать это случайно , набрав неправильно. Многие из моих учеников пытались добавить обработчик событий с именем onClickor onlickи задавались вопросом, почему он не работает. Если JavaScript выдает ошибку, это еще одна вещь, чтобы ошибиться. Во-вторых, это позволяет реализовать постоянные свойства объекта, что предотвращает изменения. Это особенно полезно на объектах.
Маннго

119

Я написал тестовый проект, который сравнивает эти 3 метода:

  • Object.freeze()
  • Object.seal()
  • Object.preventExtensions()

Мои юнит-тесты охватывают случаи CRUD:

  • [C] добавить новое свойство
  • [R] читать существующее свойство
  • [U] изменить существующее свойство
  • [D] удалить существующее свойство

Результат:

введите описание изображения здесь


2
Это великолепно. UPDATE учитывает изменение (через defineProperty) атрибутов дескриптора, например, настраиваемый, перечисляемый, доступный для записи?
Дренаи

Я всегда думал, что объекты DOM должны быть запечатаны (после полифилов, конечно). Это поможет предотвратить много опечаток.
Маннго

@Manngo Вы можете запечатать свои объекты DOM. Просто создайте DEBUGMODEпеременную и установите ее в true. Тогда делай if (DEBUGMODE) { ... }. В ..., положите функциональность для обеспечения всех объектов DOM всегда запечатаны. Затем, когда вы будете готовы распространять скрипт веб-страницы, перейдите DEBUGMODEна него false, запустите его через компилятор закрытия и распространите его. Это так просто.
Джек Гиффин,

@JackGiffin Спасибо за комментарий. Я просто говорил, что всегда думал, что это будет хорошая идея. У меня есть много студентов, которые заканчивают тем, что печатают что-то вроде element.onlick=somethingи расстраиваются, потому что это не работает, но технически это не ошибка.
Манго

2
@ Одинокий Тогда это не будет означать CRUD. Вы должны согласиться на что-то вроде RUDE;)
Manngo

84

Вы всегда можете посмотреть их в MDN. Коротко:

  • Freeze : делает объект неизменным, что означает, что изменение определенного свойства не допускается, если только они не являются объектами.
  • Печать : предотвращение добавления свойств, однако определенные свойства все еще могут быть изменены.

1
Object.seal()Также, кажется, заморозить свойства прототипа: \
K ..

10

Object.freeze()создает замороженный объект, что означает, что он берет существующий объект и, по сути, вызывает Object.seal()его, но также помечает все свойства «средства доступа к данным» writable:false, чтобы их значения не могли быть изменены. - Кайл Симпсон, вы не знаете JS - это и прототипы объектов


4

Я смотрел на различия между Freeze и Seal в ECMAScript 5 и создал сценарий, чтобы прояснить различия. Frozen создает неизменный объект, включающий данные и структуру. Печать предотвращает изменения именованных интерфейсов - не добавляет, не удаляет - но вы можете изменить объект и переопределить значение его интерфейса.

function run()
{
    var myObject = function() 
    { 
        this.test = "testing"; 
    }

    //***************************SETUP****************************

    var frozenObj = new myObject();
    var sealedObj = new myObject();

    var allFrozen = Object.freeze(frozenObj);
    var allSealed = Object.seal(sealedObj);
    alert("frozenObj of myObject type now frozen - Property test= " + frozenObj.test);
    alert("sealedObj of myObject type now frozen - Property test= " + sealedObj.test);

    //***************************FROZEN****************************

    frozenObj.addedProperty = "added Property"; //ignores add
    alert("Frozen addedProperty= " + frozenObj.addedProperty);
    delete frozenObj.test; //ignores delete
    alert("Frozen so deleted property still exists= " + frozenObj.test);
    frozenObj.test = "Howdy"; //ignores update
    alert("Frozen ignores update to value= " + frozenObj.test);
    frozenObj.test = function() { return "function"; } //ignores
    alert("Frozen so ignores redefinition of value= " + frozenObj.test);

    alert("Is frozen " + Object.isFrozen(frozenObj));
    alert("Is sealed " + Object.isSealed(frozenObj));
    alert("Is extensible " + Object.isExtensible(frozenObj));

    alert("Cannot unfreeze");
    alert("result of freeze same as the original object: " + (frozenObj === allFrozen).toString());

    alert("Date.now = " + Date.now());

    //***************************SEALED****************************

    sealedObj.addedProperty = "added Property"; //ignores add
    alert("Sealed addedProperty= " + sealedObj.addedProperty);
    sealedObj.test = "Howdy"; //allows update
    alert("Sealed allows update to value unlike frozen= " + sealedObj.test);
    sealedObj.test = function() { return "function"; } //allows
    alert("Sealed allows redefinition of value unlike frozen= " + sealedObj.test);
    delete sealedObj.test; //ignores delete
    alert("Sealed so deleted property still exists= " + sealedObj.test);
    alert("Is frozen " + Object.isFrozen(sealedObj));
    alert("Is sealed " + Object.isSealed(sealedObj));
    alert("Is extensible " + Object.isExtensible(sealedObj));

    alert("Cannot unseal");
    alert("result of seal same as the original object: " + (sealedObj === allSealed).toString());

    alert("Date.now = " + Date.now());
}

3

Я знаю, что могу немного опоздать, но

  • Сходство: оба они используются для создания нерасширяемых объектов .
  • Разница: в Freeze настраиваемые, перечисляемые и доступные для записи атрибуты объекта устанавливаются на false. где as в Запечатанном атрибуте для записи установлено значение, trueа остальные атрибуты имеют значение false.

6
Это не совсем правильно. Object.getOwnPropertyDescriptor(Object.freeze({ prop: 1 }), 'prop').enumerable=== true.
Леон Адлер

2

Теперь вы можете принудительно заморозить одно свойство объекта вместо того, чтобы заморозить весь объект. Вы можете добиться этого с Object.definePropertyс в writable: falseкачестве параметра.

var obj = {
    "first": 1,
    "second": 2,
    "third": 3
};
Object.defineProperty(obj, "first", {
    writable: false,
    value: 99
});

В этом примере obj.firstтеперь его значение зафиксировано на 99.


0

Я создал простую таблицу, чтобы сравнить приведенные ниже функции и объяснить разницу между этими функциями.

  • Object.freeze ()
  • Object.seal ()
  • Object.preventExtensions ()

таблица, которая объясняет разницу между тремя вышеуказанными методами

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.