Deskryptory nieruchomości

Większość interakcji z właściwościami obiektów będzie odbywać się na poziomie powierzchni. Dotyczy to tworzenia literałów obiektów, ustawiania i uzyskiwania dostępu do wartości właściwości za pomocą kluczy. Możesz jednak skonfigurować wewnętrznie dowolną właściwość obiektu, aby uzyskać szczegółową kontrolę nad sposobem uzyskiwania dostępu do tych właściwości, ich modyfikowania i definiowania. Każda właściwość obiektu ma zestaw niewidocznych atrybutów zawierających metadane powiązane z daną właściwością, tzw. „deskryptory właściwości”.

Z każdą właściwością powiązane są 2 rodzaje deskryptorów: deskryptory danych i deskryptory akcesorów. Deskryptor danych zawiera pary klucz-wartość, które zawierają wartość właściwości, niezależnie od tego, czy wartość ta jest możliwa do zapisu, konfigurowana czy wyliczana. Deskryptory akcesorów zawierają funkcje, które są wykonywane, gdy właściwość jest ustawiana, zmieniana lub otwierana.

Właściwość Typ deskryptora Wartość domyślna od
Object.defineProperty()
Opis
[[Value]] Dane undefined Zawiera wartość właściwości.
[[Writable]] Dane false Określa, czy możesz zmienić wartość właściwości.
[[Get]] Uzyskujący dostęp undefined Funkcja getter usługi, która jest wykonywana po uzyskaniu dostępu do właściwości.
[[Set]] Uzyskujący dostęp undefined Funkcja setter właściwości, która jest wykonywana, gdy właściwość zostanie ustawiona lub zmieniona.
[[Configurable]] Obie opcje false Jeśli to false, nie można usunąć usługi ani zmieniać jej atrybutów. Jeśli to false, a [[Writable]] ma wartość true, nadal można zmienić wartość tej właściwości.
[[Enumerable]] Obie opcje false Jeśli jest to true, możesz iterować właściwość za pomocą pętli for...in lub metody statycznej Object.keys().

Każda z tych właściwości używa takiego samego skrótu jak [[Prototype]], co wskazuje, że do tych właściwości nie należy uzyskiwać bezpośredniego dostępu. Zamiast tego do definiowania lub modyfikowania właściwości obiektu używaj metody statycznej Object.defineProperty(). Object.defineProperty() akceptuje 3 argumenty: obiekt, na którym ma nastąpić działanie, klucz właściwości do utworzenia lub zmodyfikowania oraz obiekt zawierający deskryptory powiązane z tworzonym lub modyfikowanym właściwością.

Domyślnie właściwości utworzonych za pomocą Object.defineProperty() nie można zapisywać, wyliczać ani konfigurować. Każda właściwość utworzona w ramach literału obiektu bądź w notacji w postaci kropki lub nawiasu jest możliwa do zapisu, wyliczeniowa i konfigurowalna.

const myObj = {};

Object.defineProperty(myObj, 'myProperty', {
  value: true,
  writable: false
});

myObj.myProperty;
> true

myObj.myProperty = false;

myObj.myProperty;
> true

Jeśli na przykład [[Writable]] ma wartość false, próba ustawienia nowej wartości dla powiązanej właściwości kończy się niepowodzeniem poza trybem ścisłym, a w trybie ścisłym pojawia się błąd:

{
    const myObj = {};

    Object.defineProperty(myObj, 'myProperty', {
    value: true,
    writable: false
    });

    myObj.myProperty = false;
    myObj.myProperty;
}
> true

(function () {
    "use strict";
    const myObj = {};

    Object.defineProperty(myObj, 'myProperty', {
    value: true,
    writable: false
    });

    myObj.myProperty = false;
    myObj.myProperty;
}());\
> Uncaught TypeError: "myProperty" is read-only

Efektywne użycie deskryptorów to dość zaawansowana koncepcja, ale zrozumienie wewnętrznej struktury obiektu jest niezbędne do zrozumienia składni używanych w pracy z obiektami w bardziej powszechny sposób. Te koncepcje są wykorzystywane na przykład przy użyciu metody statycznej Object.create(), która zapewnia szczegółową kontrolę nad prototypami dołączonymi do nowego obiektu.

Object.create() tworzy nowy obiekt, używając istniejącego obiektu jako prototypu. Dzięki temu nowy obiekt może dziedziczyć właściwości i metody z innego obiektu zdefiniowanego przez użytkownika w taki sam sposób, w jaki obiekty dziedziczą właściwości z wbudowanego prototypu Object w JavaScript. Wywołanie Object.create() z obiektem jako argumentem powoduje utworzenie pustego obiektu z przekazanym obiektem jako prototypem.

const myCustomPrototype = {
  'myInheritedProp': 10
};

const newObject = Object.create( myCustomPrototype );

newObject;
> Object {  }
<prototype>: Object { myInheritedProp: 10 }
  myInheritedProp: 10
  <prototype>: Object { … }

Object.create może przyjąć drugi argument określający własne właściwości nowo utworzonego obiektu przy użyciu składni podobnej do Object.defineProperty(), czyli mapowania obiektów na zbiór atrybutów deskryptora:

const myCustomPrototype = {
  'myInheritedProp': 10
};

const myObj = Object.create( myCustomPrototype, {
        myProperty: {
            value: "The new property value.",
            writable: true,
            configurable: true
        }
  });

myObj;
> Object { … }
    myProperty: "The new property value."
    <prototype>: Object { myInheritedProp: 10 }

W tym przykładzie nowy obiekt (myObj) używa literału obiektu (myCustomPrototype) jako prototypu, który zawiera odziedziczony obiekt Object.prototype. W efekcie powstaje seria dziedziczonych prototypów nazywanych łańcuchem prototypów. Każdy obiekt ma prototyp (przypisany lub odziedziczony), który ma przypisany lub odziedziczony prototyp. Ten łańcuch kończy się prototypem null, który nie ma własnego prototypu.

const myPrototype = {
  'protoProp': 10
};

const newObject = Object.setPrototypeOf( { 'objProp' : true }, myPrototype );

newObject;
> Object { objProp: true }
    objProp: true
    <prototype>: Object { protoProp: 10 }
        protoProp: 10
        <prototype>: Object { … }

Właściwości zawarte w prototypie wartości są dostępne na „najwyższym poziomie” obiektu bez konieczności bezpośredniego dostępu do właściwości prototypu:

const objectLiteral = {
    "value" : true
};

objectLiteral;
> Object { value: true }
    value: true
    <prototype>: Object { … }

objectLiteral.toString();
"[object Object]"

Ten wzorzec obowiązuje w przypadku całego łańcucha prototypu powiązanego z obiektem: podczas próby uzyskania dostępu do właściwości tłumacz szuka tej właściwości na każdym „poziomie” łańcucha prototypu, od góry do dołu, aż znajdzie właściwość lub koniec łańcucha:

const myCustomPrototype = {
  'protoProp': "Prototype property value."
};

const myObj = Object.create( myCustomPrototype, {
    myProperty: {
        value: "Top-level property value.",
        writable: true,
        configurable: true
    }
});

myObj.protoProp;
> "Prototype property value."

Sprawdź swoją wiedzę

Które deskryptory są akcesorami?

[[Get]]
[[Set]]
[[Writable]]