Nowe podejście do obiektów w ECMAScript 5. Ograniczanie możliwości modyfikacji obiektów

Ostatnio przypomniałem podstawy związane z obiektami w JavaScripcie i opisałem nowe sposoby na definiowanie własności obiektów. Dziś druga część nowości ES5 związanych z obiektem Object – zajmiemy się mrożeniem, pieczętowaniem i uniemożliwianiem rozszerzania obiektów.

ECMAScript 5: Ograniczanie możliwości modyfikacji obiektów

Mamy już zdefiniowane (w dotychczasowy sposób lub za pomocą defineProperty()) własności obiektu, ale chcielibyśmy uniemożliwić dodawanie doń nowych własności. Może nasz obiekt zawiera pewne konkretne rzeczy, od których konkretnej liczby zależy działanie innego kodu? Albo po prostu nie chcemy, żeby ktoś zaśmiecał nam nasz obiekt innymi danymi? Jak to zrobić?

ES5 (a za nim JS 1.8.5) wprowadza funkcję Object.preventExtensions(obj). Blokuje ona możliwość dodawania nowych własności do obiektu przekazanego jej jako argument. W dalszym ciągu jednakże można własności usuwać oraz modyfikować ich deskryptory.

var obj = {
    a: 5,
    b: 7
};

Object.preventExtensions(obj);

obj.a = 31337; // zmiana wartości: zadziała
delete obj.b;  // usunięcie własności: zadziała
obj.c = 42;    // dodanie własności: silent failure; w strict: TypeError

Warto jednak pamiętać, że w dalszym ciągu będzie można dodawać własności do tych obiektów znajdujących się ponad obj w łańcuchu prototypów.

Funkcja Object.isExtensible(obj) zwróci informację o tym, czy obiekt jest rozszerzalny. Jej wynikiem będzie false, jeśli na danym obiekcie wywołana została metoda Object.preventExtensions(), a true w przeciwnym przypadku (zwróćcie uwagę na to, że pyta ona o odwrotną rzecz niż to, co robi preventExtensions).

Jeśli natomiast chcielibyśmy także uniemożliwić zmienianie deskryptorów własności oraz usuwanie własności, należy obiekt „zapieczętować” – służy do tego funkcja Object.seal(obj). Robi ona wszystko to, co preventExtensions i trochę więcej: blokuje właśnie zmiany deskryptorów. Przykładowo:

var obj = {
    a: 5,
    b: 7
};

Object.seal(obj);

obj.a = 31337; // zmiana wartości: zadziała
delete obj.b;  // usunięcie własności: silent failure; w strict: TypeError
obj.c = 8;     // dodanie własności: silent failure; w strict: TypeError

Również w tym przypadku zmiany te nie będą dotyczyć własności obecnych w prototypie naszego obiektu i wyżej w łańcuchu prototypów.

Funkcja Object.isSealed(obj) zwróci informację o tym, czy obiekt jest zapieczętowany. Jej wynikiem będzie true, jeśli na danym obiekcie wywołana została metoda Object.seal(), a false w przeciwnym przypadku.

Żeby całkowicie uniemożliwić manipulacje własnościami obiektu należy go zamrozić. Zamrożenie oznacza, że własności tego obiektu stają się własnościami tylko do odczytu, a obiekt staje się niemodyfikowalny. Służy do tego metoda Object.freeze(obj):

var obj = {
    a: 5,
    b: 7
};

Object.freeze(obj);

obj.a = 31337; // zmiana wartości: silent failure; w strict: TypeError
delete obj.b;  // usunięcie własności: silent failure; w strict: TypeError
obj.c = 8;     // dodanie własności: silent failure; w strict: TypeError

Także i tutaj nie będą dotyczyć własności obecnych w prototypie naszego obiektu i wyżej w łańcuchu prototypów.

Funkcja Object.isFrozen(obj) zwróci informację o tym, czy obiekt jest zamrożony. Jej wynikiem będzie true, jeśli na danym obiekcie wywołana została metoda Object.freeze(), a false w przeciwnym przypadku.

Jeśli nasz obiekt zawiera referencje do innych obiektów (w tym tablic), to należy pamiętać, że w dalszym ciągu będzie można operować na tych obiektach:

var obj = {
    subArr: [1, 2, 3],
    subObj: {
        x: 5,
        y: 7
    }
};

Object.freeze(obj);

alert(Object.isFrozen(obj)); // true

alert(obj.subArr[1]); // "2"

obj.subArr[1] = 5;
alert(obj.subArr[1]); // "5"

delete obj.subObj.x;
alert(JSON.stringify(obj.subObj)); // '{"y":7}'

W powyższym przykładzie, gdyby zależało nam na niemodyfikowalności obiektów będących własnościami obiektu obj, należałoby wywołać freeze() także na obj.subArr i obj.subObj.

Poniżej porównanie trzech opisanych wyżej funkcji w formie tabelki:

metoda Object.*\operacje zmiana wartości własności (prze-)konfigurowanie własności dodawanie nowych własności
(brak) TAK TAK TAK
preventExtensions() TAK TAK NIE
seal() TAK NIE NIE
freeze() NIE NIE NIE

To tyle w tym odcinku. W kolejnym będziemy tworzyć nowe obiekty w łańcuchu prototypów, ale unikając operatora new.

One thought on “Nowe podejście do obiektów w ECMAScript 5. Ograniczanie możliwości modyfikacji obiektów

Leave a comment