Różnica między zamrożeniem a uszczelnieniem

164

Właśnie usłyszałem o metodach JavaScript freezei seal, które można wykorzystać do uczynienia dowolnego obiektu niezmiennym.

Oto krótki przykład, jak go używać:

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

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

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

Jaka jest różnica między freezei seal? Czy mogą zwiększyć wydajność?

maja
źródło
6
Tylko uwaga dla każdego, kto patrzy na to pytanie, przyjęta odpowiedź jest niezgodna ze stanem faktycznym. Odpowiedź @ tungd jest poprawna.
Bjorn
2
Kolejna uwaga, Object.preventExtensionsoprócz Object.seali Object.freeze. Object.preventExtensionspo prostu zapobiega dodawaniu nowych elementów do obiektu. Możesz usuwać, konfigurować i zmieniać wartości właściwości obiektów, dla których wyłączono ich rozszerzalność za pomocą Object.preventExtensions.
Bjorn

Odpowiedzi:

193

Object.seal

  • Zapobiega dodawaniu i / lub usuwaniu właściwości z zapieczętowanego obiektu; użycie deletezwróci fałsz
  • Sprawia, że żadna istniejąca właściwość nie jest konfigurowalna : nie można ich konwertować z `` deskryptorów danych '' na `` deskryptory akcesorów '' (i odwrotnie), a żaden atrybut deskryptorów akcesorów nie może być w ogóle modyfikowany (podczas gdy deskryptory danych mogą zmieniać swój writableatrybut, oraz ich valueatrybut, jeśli writeablejest prawdziwy).
  • Może rzucić a TypeErrorpodczas próby zmodyfikowania wartości samego zapieczętowanego obiektu (najczęściej w trybie ścisłym )

Object.freeze

  • Dokładnie to Object.seal, co robi, a ponadto:
  • Zapobiega modyfikacji żadnych istniejących obiektów

Żaden z nich nie wpływa na obiekty „głębokie” / wnuki. Np. Jeśli objjest zamrożone, obj.elnie można go ponownie przypisać, ale wartość obj.elmożna zmodyfikować, np. obj.el.idMożna ją zmienić.


Występ:

Zapieczętowanie lub zamrożenie obiektu może wpłynąć na szybkość jego wyliczania w zależności od przeglądarki:

  • Firefox: nie ma to wpływu na wydajność wyliczania
  • IE: wpływ na wydajność wyliczenia jest znikomy
  • Chrome: wydajność wyliczania jest szybsza w przypadku obiektów zamkniętych lub zamrożonych
  • Safari: zamknięte lub zamrożone obiekty wyliczają 92% wolniej (od 2014 r.)

Testy: Sealed obiekty , zamrożone obiekty .

Niccolò Campolungo
źródło
2
Czy możesz porozmawiać o tym, dlaczego mielibyśmy kiedykolwiek używać tych metod? Tylko dlatego, że możemy?
Alan Dong
3
Myślę, że w przyszłości będą one często używane (jeśli zostaną odpowiednio zoptymalizowane) podczas tworzenia biblioteki / frameworka. Pozwalają one zapobiec (nawet niezamierzonemu) użytkownikowi złamania kodu (i, jak stwierdzono w odpowiedzi, optymalizacje powinny prowadzić do znacznej poprawy szybkości). Ale to czysta spekulacja :)
Niccolò Campolungo
2
Ta odpowiedź zawiera wiele błędów rzeczowych. Po pierwsze, sealsprawia , że istniejące właściwości nie są konfigurowalne, patrz jsfiddle.net/btipling/6m743whn Numer 2, nadal można edytować, to znaczy zmieniać wartości istniejących właściwości w zamkniętym obiekcie.
Bjorn
8
FWIW, zamrożone i zapieczętowane obiekty są teraz szybsze niż ich niezamrożone i niezapieczętowane odpowiedniki w Chrome Canary w wersji 43.0.2317.0.
llambda
2
@AlanDong Trochę za późno, ale oto dlaczego chcesz zablokować obiekt. Jedną z cech JavaScript jest to, że możesz dodać właściwość w dowolnym momencie; możesz to również zrobić przypadkowo , błędnie wpisując. Wielu moich uczniów próbowało dodać program obsługi zdarzeń o nazwie onClicklub onlicki zastanawiali się, dlaczego nie działa. Jeśli JavaScript zgłasza błąd, to o jedną rzecz mniej do popełnienia błędu. Po drugie, pozwala to na zaimplementowanie stałych właściwości obiektu, co zapobiega zmianom. Jest to szczególnie przydatne w przypadku metod obiektowych.
Manngo,
119

Napisałem projekt testowy, który porównuje te 3 metody:

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

Moje testy jednostkowe obejmują przypadki CRUD:

  • [C] dodaj nową właściwość
  • [R] odczyt istniała własność
  • [U] zmodyfikuj istniejącą właściwość
  • [D] usuń istniejącą właściwość

Wynik:

wprowadź opis obrazu tutaj

piecioshka
źródło
2
To jest genialne. Czy UPDATE uwzględnia modyfikację (poprzez defineProperty) atrybutów deskryptora, np. Konfigurowalne, wyliczalne, zapisywalne?
Drenai,
Zawsze uważałem, że obiekty DOM powinny być zapieczętowane (oczywiście po polyfillach). Pomogłoby to uniknąć wielu błędów literówek.
Manngo,
@Manngo Możesz zapieczętować swoje obiekty DOM. Po prostu utwórz DEBUGMODEzmienną i ustaw ją na true. Następnie zrób if (DEBUGMODE) { ... }. W programie ...umieść swoją funkcjonalność zapewniającą, że wszystkie obiekty DOM są zawsze zapieczętowane. Następnie, gdy jesteś gotowy do dystrybucji skryptu strony internetowej, zmień DEBUGMODEgo false, uruchom skrypt za pomocą kompilatora zamknięcia i rozpowszechnij go. To takie proste.
Jack Giffin,
@JackGiffin Dzięki za komentarz. Mówiłem tylko, że zawsze uważałem, że to dobry pomysł. Mam wielu uczniów, którzy w końcu piszą coś podobnego element.onlick=somethingi są sfrustrowani, ponieważ to nie działa, ale technicznie nie jest to błąd.
Manngo,
2
@ Samotny Wtedy nie przeliterowałoby CRUD. Musiałbyś zadowolić się czymś w rodzaju RUDE;)
Manngo
84

Zawsze możesz je sprawdzić w MDN. W skrócie:

  • Freeze : sprawia, że ​​obiekt jest niezmienny, co oznacza, że ​​żadna zmiana zdefiniowanej właściwości nie jest dozwolona, ​​chyba że są to obiekty.
  • Pieczęć : zapobiega dodawaniu właściwości, jednak zdefiniowane właściwości nadal można zmienić.
tungd
źródło
1
Object.seal()również wydaje się zamrażać właściwości prototypu: \
K ..
10

Object.freeze()tworzy zamrożony obiekt, co oznacza, że ​​pobiera istniejący obiekt i zasadniczo go wywołuje Object.seal(), ale także oznacza wszystkie właściwości „ elementu dostępu do danych” jako writable:false, aby nie można było zmienić ich wartości. - Kyle Simpson, You Don't Know JS - This & Object Prototypes

shmuli
źródło
4

Patrzyłem na różnice między Freeze i Seal w ECMAScript 5 i stworzyłem skrypt, aby wyjaśnić różnice. Frozen tworzy niezmienny obiekt, w tym dane i strukturę. Pieczęć zapobiega zmianom w nazwanych interfejsach - nie dodaje, nie usuwa - ale możesz zmutować obiekt i przedefiniować znaczenie jego interfejsu.

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());
}
Jaycee
źródło
3

Wiem, że mogę się trochę spóźnić, ale

  • Podobieństwo: oba są używane do tworzenia obiektów nierozszerzalnych .
  • Różnica: W konfigurowalnych Freeze , wyliczalne i zapisywalne atrybuty obiektu są ustawione na false. gdzie jak w Sealed writable atrybut jest ustawiony na, truea pozostałe atrybuty są false.
Faisal Naseer
źródło
6
To nie jest całkowicie poprawne. Object.getOwnPropertyDescriptor(Object.freeze({ prop: 1 }), 'prop').enumerable=== true.
Leon Adler
2

Możesz teraz wymusić zamrożenie pojedynczej właściwości obiektu zamiast zamrażania całego obiektu. Można to osiągnąć z Object.definePropertyze writable: falsejako parametr.

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

W tym przykładzie obj.firstjego wartość jest teraz zablokowana na 99.

jaggedsoft
źródło
0

Stworzyłem prostą tabelę, aby porównać poniższe funkcje i wyjaśnić różnicę między tymi funkcjami.

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

tabela wyjaśniająca różnicę między powyższymi trzema metodami

Shwetabh Shekhar
źródło