Blog webdeveloperski Patryk yarpo Jar

Tworzenie obiektów za pomocą Object.create (ECMAScript 5)

Autor wiadomości Czerwiec 10, 2013

JavaScript nie stoi w miejscu. Cały czas trwają prace nad różnymi API wchodzącymi w skład HTML 5, jak również nad rdzeniem języka - ECMAScript.

Dziś coś o nowości z ECMAScript 5 metodzie `Object.create' pozwalającej na wygodne tworzenie obiektów. Przedstawiłem już wiele sposobów na tworzenie obiektów w JavaScript. Większość z nich była jednak mniej lub bardziej naginaniem podstawowych mechanizmów języka,aby przybliżyć jego działanie do oczekiwań programistów Java-pochodnych (pamiętajmy, nawet potocznie Java != JavaScript).

Object.create - szybki start

Do tej pory, aby stworzyć obiekt wykonywało się taki kod:

var obj = {}; // lub obj = new Object;

Teraz można zrobić tak:

var obj = Object.create(Object.prototype);

Bez sensu... Dłuższy zapis, a uzyskaliśmy to samo. Tak, prawda. W tym wypadku jest to bez sensu. Jednak `Object.create' ma opcje, o których nie można było nawet marzyć dawniej.

writable, enumerable, configurable

Tworząc obiekty za pomocą `Object.create' możemy określić dawniej niedostępne cechy pola obiektu, takie jak:

  • writable - można zmieniać wartość pola
  • enumerable - pole będzie brane pod uwagę w pętlach for in
  • configurable - czy można usunąć to pole z obiektu (za pomocą operatora `delete') oraz czy można zmienić ustawienia cech pola (używam słowa "cecha" choć na stronach dokumentacji Mozilli spotyka się określenie "attribute".)

Pewnie wydaje Ci się to niezrozumiałe. Przykład na pewno pomoże:

var obj = Object.create(Object.prototype, {
    abc : {
       value : 0,
       writable : true,
       enumerable : true,
       configurable : true
    }
});

obj.abc = 100; // mozna zmienic wartosc
console.log(obj.abc); // wartosc: 100

for(var key in obj) {
    console.log(key, obj[key]); // 'abc', 100
}

delete obj.abc;
console.log(obj.abc); // undefined

A teraz ten sam kod, tylko ze zmienionymi wartościami na `false':

var obj = Object.create(Object.prototype, {
    abc : {
       value : 0,
       writable : false,
       enumerable : false,
       configurable : false
    }
});

obj.abc = 100; // nie ustawi nowej wartosci (ale nie rzuci bledem)
console.log(obj.abc); // 0

for(var key in obj) {
    console.log(key, obj[key]); // w ogole tu nie zajrzy
}

delete obj.abc;
console.log(obj.abc); // 0

Czy już teraz zaczyna Ci się to podobać? 🙂

Domyślne wartości

Jeśli nie określisz wartości tych cech (atrybutów właściwości obiektu) to przyjmą one domyślne wartości:

  • writable : false
  • enumerable : false
  • configurable : false

Trochę mnie to dziwi, bo przecież chyba częściej tworzy się pola, które chcemy, aby były możliwe do zmiany.

setter i getter

Początkowo sadziłem, że metody `get'/`set' będą działać dokładnie tak jak w znanych mi językach. Z własnych prób i z przykładów na różnych stronach wynika jednak co innego. Posiadając już wiedzę zaczerpniętą z powyższych przykładów taki kod powinien być dla każdego zrozumiały (i moim zdaniem poprawny):

var obj = Object.create(Object.prototype, {
    abc : {
        value : 1,
        get : function() { return value; }, // lub this.value
        set : function(v) { this.value = v; }
    }
});

Ten kod nie działa. występują błąd: "property descriptors must not specify a value or be writable when a getter or setter has been specified". Czyli jeśli chcemy uzywać get/set musimy nie podawać pola `value'.  Co zatem zrobić? Należy odwoływać się tu do pola po jego nazwie:

var obj = Object.create(Object.prototype, {
    abc : {
        get : function() { return abc; },
        set : function(v) { abc = v; }
    }
});

Zalety (IMO)

Zaletą jest to, że JS doczekał się swojego własnego sposobu definiowania obiektów. Nie udaje już więcej C++/Javy w pod tym względem, ani nie "wstydzi" się prototypów. Ciekaw jestem jak będzie wyglądać sprawa wydajności w porównaniu z własnymi implementacjami modułów/obiektów. Cieszę się także, że dodano możliwość definiowania `writable', `configurable' oraz `enumerable'.

Wady (IMVHO)

1. Zapis jest zbyt długi. Jeśli chcemy zrobić zwykły obiekt z jednym polem musimy wykonać kod:

var o = Object.create(null, {
    x : {
        value : 1,
        writable : true // bez tego wpisu nie bedzie mozna zmieniac wartosci
    }
});

zamiast:

var o = { x : 1 }

Dodatkowo pierwszy kod powinien mieć także zdefiniowane `configurable' i `enumerable', które domyślnie mają wartość `false'. Odnoszę wrażenie, że częściej jednak chcemy zmieniać wartość jakiegoś pola i częściej chcemy, aby było widoczne.

2. Brak komunikatu o poniechaniu żądanej akcji. Kiedy mamy obiekt z domyślnymi ustawieniami pól i spróbujemy zmienić wartość jednego z nich, to oczywiście nie uda się nam to. Nie zostaniemy jednak w żaden sposób o tym poinformowani. Widzę już dodatkowe linie kodu sprawdzające nie tylko, czy istnieje pole w obiekcie, ale czy można w nim zapisywać.

Całkiem możliwe, że nie mam racji. Może po prostu patrzę na to zbyt przyziemnie. W większych zastosowaniach proponowany mechanizm może się okazać niezastąpiony. Nie wątpię, że tak może być. Muszę jednak przyznać, że mnie na chwilę obecną raczej rozczarował.

Warto przeczytać

Komentarze (4) Trackbacks (0)
  1. Myślę, że to jest takie na siłę javascriptowe. Mogli wprowadzić po prostu definiowanie pół w stylu

    var o = {
    [w,c,e] name : value
    }

    albo jakieś:
    var o = {
    +*~ name : value
    }

    Może mniej czytelne, ale jak ktoś pierwszy raz widzi for(a : list) innych językach to też nie musi od razu wiedzieć, co to robi 🙂

    Nie mówiąc o tym, że takie definiowanie obiektów jest mało podatne na kompresję, a kody JS są coraz dłuższe, więc kompresja jest coraz popularniejsza.

  2. owszem, niby mogliby, ale zauważ, że ES5 z założenia nie miał zrywać kompatybilności ze składnią ES3. Stąd zapewne takie a nie inne rozwiązania

  3. no dobra, ale w pierwszym parametrze wcale nie trzeba podawać Object.prototype. Siła funkcji Object.create polega min. na tym, że możesz utworzyć nowy obiekt na podstawie dowolnego innego, który będzie jego prototypem. Czyli nie tylko możesz utworzyć nowy obiekt, ale też możesz w prosty sposób wprowadzić dziedziczenie…

    Ale z tym, że drugi argument powinien być prostszy też się zgadzam. writable, enumerable, configurable – jak często się tego używa?

  4. Nie wiem (a D. Crockford mnie w tym wspiera), czy JS potrzebuje mechanizmu dziedziczenia.

    Choc jest dokladnie tak jak mowisz – mozna podac dowolny obiekt. Widzialem gdzies notacje obiektowa (nie wiem, czy to byla tylko propozycja, czy gotowy format), ktory mial pozwalac na definiowanie obiektow. Taki syntaxy sugar dla Object.create.

    No zobaczymy, w ktora strone to bedzie isc 🙂


Leave a comment

 

Brak trackbacków.