in deze blogpost bekijken we hoe de ECMAScript-specificatie JavaScript-objecten ziet. In het bijzonder, eigenschappen zijn niet atomisch in de spec, maar samengesteld uit meerdere attributen (denk velden in een record). Zelfs de waarde van een data-eigenschap wordt opgeslagen in een attribuut!
inhoudsopgave:
- de structuur van objecten #
- interne slots #
- Eigenschappentoetsen #
- Eigenschapkenmerken #
- Property descriptors #
- descriptoren ophalen voor eigenschappen #
- nieuwe eigenschappen aanmaken via descriptoren #
- bestaande eigenschappen wijzigen via descriptoren #
- valkuil: overgenomen alleen-lezen eigenschappen kunnen niet worden toegewezen aan #
- API: property descriptors #
- Use cases for Object.getOwnPropertyDescriptors () #
- Object.getOwnPropertyDescriptors (): eigenschappen kopiëren naar een object #
- valkuil: kopieermethoden die super #
- Object.getOwnPropertyDescriptors (): cloning objects #
de structuur van objecten #
In de ECMAScript-specificatie bestaat een object uit:
- interne slots, dat zijn opslaglocaties die niet toegankelijk zijn vanuit JavaScript, alleen voor bewerkingen in de specificatie.
- een verzameling eigenschappen. Elke eigenschap associeert een sleutel met attributen (denk velden in een record).
interne slots #
Dit is hoe de specificatie interne slots beschrijft (de nadruk ligt op mij):
- interne slots komen overeen met de interne toestand die is geassocieerd met objecten en wordt gebruikt door verschillende ECMAScript-specificatie-algoritmen.
- interne sleuven zijn geen objecteigenschappen en ze worden niet overgenomen.
- afhankelijk van de specifieke interne slotspecificatie kan een dergelijke toestand bestaan uit waarden:
- van elk type ECMAScript-taal of
- van specifieke waarden van het type ECMAScript-specificatie.
- tenzij expliciet anders aangegeven, worden interne slots toegewezen als onderdeel van het proces van het maken van een object en mogen ze niet dynamisch aan een object worden toegevoegd.
- tenzij anders aangegeven, is de beginwaarde van een intern slot de waarde
undefined
. - verschillende algoritmen binnen deze specificatie maken objecten met interne sleuven. De ECMAScript-taal biedt echter geen directe manier om interne slots te associëren met een object.
- interne methoden en interne sleuven worden binnen deze specificatie geïdentificeerd aan de hand van Namen tussen dubbele vierkante haken
]
.
Er zijn twee soorten interne sleuven:
- Method sleuven voor het manipuleren van objecten (eigenschappen ophalen, eigenschappen instellen, enz.)
- Data slots with storage (listed in the table below)
Internal data slot | Type |
---|---|
] |
null | object |
] |
boolean |
] |
List of entries |
Descriptions for these data slots:
Eigenschappentoetsen #
de sleutel van een eigenschap is ofwel:
- een tekenreeks
- een symbool
Eigenschapkenmerken #
Er zijn twee soorten eigenschappen die verschillende kenmerken hebben:
- een gegevenseigenschap slaat gegevens op. De attributen
value
bevatten elke JavaScript-waarde. - een accessor-eigenschap heeft een getter-functie en / of een setter-functie. De eerste wordt opgeslagen in het attribuut
get
, de laatste in het attribuutset
.
de volgende tabel toont alle eigenschappen.
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
bepaalt of de waarde van een gegevenseigenschap kan worden gewijzigd. -
configurable
bepaalt of de attributen van een eigenschap kunnen worden gewijzigd. Als hetfalse
is, dan:- u kunt de eigenschap niet verwijderen.
- u kunt een eigenschap niet wijzigen van een data-eigenschap naar een accessor-eigenschap of omgekeerd.
- u kunt geen andere attributen wijzigen dan
value
. - echter, één attribuutwijziging is toegestaan: U kunt
writable
veranderen van naarfalse
. De reden achter deze anomalie is historisch: eigenschap.length
van Arrays is altijd beschrijfbaar en niet-configureerbaar geweest. Als hetwritable
attribuut gewijzigd wordt, kunnen we Arrays bevriezen.
-
enumerable
beïnvloedt sommige bewerkingen (zoalsObject.assign()
). Als hetfalse
is, dan negeren deze bewerkingen de eigenschap.
Property descriptors #
een property descriptor codeert de attributen van een property als een JavaScript-object. Hun TypeScript interfaces zien er als volgt uit.
de vraagtekens geven aan dat elke eigenschap optioneel is. Als u een eigenschap weglaat bij het doorgeven van een descriptor aan een operatie, dan wordt de standaardwaarde gebruikt.
descriptoren ophalen voor eigenschappen #
de volgende code haalt de objectdescriptor op voor de gegevenseigenschap first
:
in het volgende voorbeeld halen we de eigenschap descriptor op voor de getter fullName
:
met behulp van desc()
In regel A is een work-around zodat .deepEqual()
werkt.
nieuwe eigenschappen aanmaken via descriptoren #
u kunt ook nieuwe eigenschappen aanmaken via eigenschapsdescriptoren:
bestaande eigenschappen wijzigen via descriptoren #
als een eigen eigenschap al bestaat, wijzigt het definiëren via een descriptor die eigenschap. Aan de ene kant kunnen we Object.defineProperty()
– achtige toewijzing gebruiken:
aan de andere kant kunnen we ook Object.defineProperty()
gebruiken om een gegevenseigenschap om te zetten in een getter (en vice versa):
valkuil: overgenomen alleen-lezen eigenschappen kunnen niet worden toegewezen aan #
als een overgenomen eigenschap alleen-lezen is, dan kunnen we geen toewijzing gebruiken om het te veranderen. De grondgedachte is dat het overschrijven van een geërfd eigendom door het creëren van een eigen eigendom kan worden gezien als een niet-destructief veranderen van het geërfd eigendom. Als een eigenschap niet beschrijfbaar is, zouden we dat niet moeten kunnen doen.
laten we eens kijken naar een voorbeeld:
We kunnen de eigenschap niet wijzigen via toewijzing. Maar we kunnen nog steeds een eigen eigenschap maken door deze te definiëren:
Object.defineProperty(obj, 'prop', {value: 2});assert.equal(obj.prop, 2);
Accessor-eigenschappen die geen setter hebben, worden ook als alleen-lezen beschouwd:
API: property descriptors #
met de volgende functies kunt u werken met property descriptors:
Use cases for Object.getOwnPropertyDescriptors () #
Object.getOwnPropertyDescriptors (): eigenschappen kopiëren naar een object #
sinds ES6 heeft JavaScript al een gereedschapsmethode voor het kopiëren van eigenschappen: Object.assign()
. Deze methode gebruikt echter eenvoudige get-en set-bewerkingen om een eigenschap te kopiëren waarvan de sleutel key
:
target = source;
dat betekent dat het alleen een getrouwe kopie van een eigenschap maakt als:
- het attribuut
writable
is en zijn attribuutenumerable
is (omdat dat is hoe toewijzing eigenschappen creëert). - Het is een gegevenseigenschap.
het volgende voorbeeld illustreert deze beperking. Object source
heeft een setter waarvan de sleutel data
is.
als we Object.assign()
gebruiken om eigenschap data
te kopiëren, dan wordt de eigenschap data
geconverteerd naar een gegevenseigenschap:
Gelukkig wordt Object.getOwnPropertyDescriptors()
samen met Object.defineProperties()
kopieert de eigenschap getrouw data
:
valkuil: kopieermethoden die super
#
een methode die super
gebruikt, is stevig verbonden met het eigen object (het object waarin het is opgeslagen). Er is momenteel geen manier om een dergelijke methode te kopiëren of te verplaatsen naar een ander object.
Object.getOwnPropertyDescriptors (): cloning objects #
ondiep klonen is vergelijkbaar met kopieereigenschappen, daarom is Object.getOwnPropertyDescriptors()
ook hier een goede keuze.
om de kloon te maken, gebruiken we Object.create()
: