In questo post del blog, diamo un’occhiata più da vicino a come le specifiche ECMAScript vedono gli oggetti JavaScript. In particolare, le proprietà non sono atomiche nelle specifiche, ma composte da più attributi (si pensi ai campi in un record). Anche il valore di una proprietà dati viene memorizzato in un attributo!
Sommario:
- La struttura degli oggetti #
- Slot interni #
- Proprietà tasti #
- gli attributi di Proprietà #
- Descrittori di proprietà #
- Recupero dei descrittori per le proprietà #
- Creazione di nuove proprietà tramite descrittori #
- Modifica delle proprietà esistenti tramite descrittori #
- Pitfall: le proprietà ereditate di sola lettura non possono essere assegnate a #
- API: property descriptors #
- Use case for Object.getOwnPropertyDescriptors () #
- Oggetto.getOwnPropertyDescriptors (): copiare le proprietà in un oggetto #
- Insidia: metodi di copia che utilizzano super #
- Oggetto.getOwnPropertyDescriptors (): clonazione di oggetti #
La struttura degli oggetti #
Nella specifica ECMAScript, un oggetto è costituito da:
- Slot interni, che sono posizioni di archiviazione non accessibili da JavaScript, solo per le operazioni nella specifica.
- Una raccolta di proprietà. Ogni proprietà associa una chiave con attributi (si pensi campi in un record).
Slot interni #
Questo è il modo in cui la specifica descrive gli slot interni (l’enfasi è mia):
- Gli slot interni corrispondono allo stato interno associato agli oggetti e utilizzato da vari algoritmi di specifica ECMAScript.
- Gli slot interni non sono proprietà dell’oggetto e non sono ereditati.
- A seconda della specifica specifica dello slot interno, tale stato può essere costituito da valori:
- di qualsiasi tipo di linguaggio ECMAScript o
- di specifici valori del tipo di specifica ECMAScript.
- Se non diversamente specificato, gli slot interni sono allocati come parte del processo di creazione di un oggetto e non possono essere aggiunti dinamicamente a un oggetto.
- Se non diversamente specificato, il valore iniziale di uno slot interno è il valore
undefined
. - Vari algoritmi all’interno di questa specifica creano oggetti con slot interni. Tuttavia, il linguaggio ECMAScript non fornisce alcun modo diretto per associare gli slot interni a un oggetto.
- I metodi interni e gli slot interni sono identificati all’interno di questa specifica utilizzando nomi racchiusi tra doppie parentesi quadre
]
.
Esistono due tipi di slot interni:
- Slot di metodo per manipolare oggetti (ottenere proprietà, impostare proprietà, ecc.)
- Data slots with storage (listed in the table below)
Internal data slot | Type |
---|---|
] |
null | object |
] |
boolean |
] |
List of entries |
Descriptions for these data slot:
Proprietà tasti #
La chiave di una proprietà è:
- stringa
- simbolo
gli attributi di Proprietà #
Ci sono due tipi di proprietà e hanno diversi attributi:
- Una proprietà di dati memorizza i dati. I suoi attributi
value
contengono qualsiasi valore JavaScript. - Una proprietà accessor ha una funzione getter e / o una funzione setter. Il primo è memorizzato nell’attributo
get
, il secondo nell’attributoset
.
La seguente tabella elenca tutti gli attributi delle proprietà.
Kind of property | Name and type of attribute | Default value |
---|---|---|
Data property | value: any |
undefined |
writable: boolean |
false |
|
Accessor property | get(): any |
undefined |
set(v: any): void |
undefined |
|
All properties | configurable: boolean |
false |
enumerable: boolean |
false |
We have already encountered the attributes value
get
, and set
. The other attributes work as follows:
-
writable
determina se è possibile modificare il valore di una proprietà dati. -
configurable
determina se gli attributi di una proprietà possono essere modificati. Se si tratta difalse
, allora:- Non è possibile eliminare la proprietà.
- Non è possibile modificare una proprietà da una proprietà data a una proprietà accessor o viceversa.
- Non è possibile modificare alcun attributo diverso da
value
. - Tuttavia, è consentita un’altra modifica di attributo: È possibile modificare
writable
datrue
afalse
. La logica alla base di questa anomalia è storica: la proprietà.length
degli array è sempre stata scrivibile e non configurabile. Permettendo la suawritable
attributo da modificare ci permette di congelare array.
-
enumerable
influenza alcune operazioni (ad esempioObject.assign()
). Se si tratta difalse
, tali operazioni ignorano la proprietà.
Descrittori di proprietà #
Un descrittore di proprietà codifica gli attributi di una proprietà come oggetto JavaScript. Le loro interfacce dattiloscritte sono le seguenti.
I punti interrogativi indicano che ogni proprietà è facoltativa. Se si omette una proprietà quando si passa un descrittore a un’operazione, viene utilizzato il suo valore predefinito.
Recupero dei descrittori per le proprietà #
Il codice seguente recupera il descrittore dell’oggetto per la proprietà dei dati first
:
Nel prossimo esempio, recuperiamo il descrittore di proprietà per il getter fullName
:
Usando desc()
nella riga A è un work-around in modo che .deepEqual()
funzioni.
Creazione di nuove proprietà tramite descrittori #
È anche possibile creare nuove proprietà tramite descrittori di proprietà:
Modifica delle proprietà esistenti tramite descrittori #
Se esiste già una proprietà propria, la definizione tramite un descrittore modifica tale proprietà. Da un lato che ci permette di usareObject.defineProperty()
come assegnazione:
D’altra parte, possiamo anche usare Object.defineProperty()
per trasformare una proprietà data in un getter (e viceversa):
Pitfall: le proprietà ereditate di sola lettura non possono essere assegnate a #
Se una proprietà ereditata è di sola lettura, allora non possiamo usare l’assegnazione per cambiarla. La logica è che l’override di una proprietà ereditata creando una proprietà propria può essere vista come una modifica non distruttiva della proprietà ereditata. Probabilmente, se una proprietà non è scrivibile, non dovremmo essere in grado di farlo.
Diamo un’occhiata a un esempio:
Non possiamo modificare la proprietà tramite assegnazione. Ma possiamo ancora creare una proprietà definendola:
Object.defineProperty(obj, 'prop', {value: 2});assert.equal(obj.prop, 2);
Anche le proprietà di accesso che non hanno un setter sono considerate di sola lettura:
API: property descriptors #
Le seguenti funzioni consentono di lavorare con i descrittori di proprietà:
Use case for Object.getOwnPropertyDescriptors () #
Oggetto.getOwnPropertyDescriptors (): copiare le proprietà in un oggetto #
Da ES6, JavaScript ha già un metodo tool per copiare le proprietà:Object.assign()
. Tuttavia, questo metodo utilizza semplice ottenere e impostare le operazioni di copia di un immobile la cui chiave è key
:
target = source;
il che significa Che non solo crea una copia fedele di un immobile se:
- l’attributo
writable
true
e l’attributo dienumerable
true
(perché è così che l’assegnazione crea le proprietà). - Si tratta di una proprietà di dati.
Il seguente esempio illustra questa limitazione. Object source
ha un setter la cui chiave èdata
.
Se si usa Object.assign()
per copiare la struttura data
, quindi la funzione di accesso di proprietà data
viene convertito in una proprietà di dati:
Fortunatamente, utilizzando Object.getOwnPropertyDescriptors()
con Object.defineProperties()
non fedelmente copia la proprietà data
:
Insidia: metodi di copia che utilizzano super
#
Un metodo che utilizzasuper
è saldamente connesso con il suo oggetto home (l’oggetto in cui è memorizzato). Al momento non è possibile copiare o spostare tale metodo su un oggetto diverso.
Oggetto.getOwnPropertyDescriptors (): clonazione di oggetti #
La clonazione superficiale è simile alle proprietà di copia, motivo per cuiObject.getOwnPropertyDescriptors()
è una buona scelta anche qui.
Per creare il clone, usiamo Object.create()
: