9.1 The structure of objects
In the ECMAScript specification, an object consists of:
- Internal slots, which are storage locations that are not accessible from JavaScript, only from operations in the specification.
- A collection of properties. Each property associates a key with attributes (think fields in a record).
9.1.1 Internal slots
The specification describes internal slots as follows. I added bullet points and emphasized one part:
- Internal slots correspond to internal state that is associated with objects and used by various ECMAScript specification algorithms.
- Internal slots are not object properties and they are not inherited.
- Depending upon the specific internal slot specification, such state may consist of:
- values of any ECMAScript language type or
- of specific ECMAScript specification type values.
- Unless explicitly specified otherwise, internal slots are allocated as part of the process of creating an object and may not be dynamically added to an object.
- Unless specified otherwise, the initial value of an internal slot is the value
undefined
. - Various algorithms within this specification create objects that have internal slots. However, the ECMAScript language provides no direct way to associate internal slots with an object.
- Internal methods and internal slots are identified within this specification using names enclosed in double square brackets
[[ ]]
.
There are two kinds of internal slots:
- Method slots for manipulating objects (getting properties, setting properties, etc.)
- Data slots that store values.
Ordinary objects have the following data slots:
.[[Prototype]]: null | object
- Stores the prototype of an object.
- Can be accessed indirectly via
Object.getPrototypeOf()
andObject.setPrototypeOf()
.
.[[Extensible]]: boolean
- Indicates if it is possible to add properties to an object.
- Can be set to
false
viaObject.preventExtensions()
.
.[[PrivateFieldValues]]: EntryList
- Is used to manage private class fields.
9.1.2 Property keys
The key of a property is either:
- A string
- A symbol
9.1.3 Property attributes
There are two kinds of properties and they are characterized by their attributes:
- A data property stores data. Its attribute
value
holds any JavaScript value. - An accessor property consists of a getter function and/or a setter function. The former is stored in the attribute
get
, the latter in the attributeset
.
Additionally, there are attributes that both kinds of properties have. The following table lists all attributes and their default values.
Kind of property | Name and type of attribute | Default value |
---|---|---|
Data property | value: any | undefined |
writable: boolean | false | |
Accessor property | get: (this: any) => any | undefined |
set: (this: any, 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
determines if the value of a data property can be changed.configurable
determines if the attributes of a property can be changed. If it isfalse
, then:- We cannot delete the property.
- We cannot change a property from a data property to an accessor property or vice versa.
- We cannot change any attribute other than
value
. - However, one more attribute change is allowed: We can change
writable
fromtrue
tofalse
. The rationale behind this anomaly is historical: Property.length
of Arrays has always been writable and non-configurable. Allowing itswritable
attribute to be changed enables us to freeze Arrays.
enumerable
influences some operations (such asObject.keys()
). If it isfalse
, then those operations ignore the property. Most properties are enumerable (e.g. those created via assignment or object literals), which is why you’ll rarely notice this attribute in practice. If you are still interested in how it works, see [content not included].
9.1.3.1 Pitfall: Inherited non-writable properties prevent creating own properties via assignment
If an inherited property is non-writable, we can’t use assignment to create an own property with the same key:
const proto = {
prop: 1,
};
// Make proto.prop non-writable:
Object.defineProperty(
proto, 'prop', {writable: false});
const obj = Object.create(proto);
assert.throws(
() => obj.prop = 2,
/^TypeError: Cannot assign to read only property 'prop'/);
For more information, see [content not included].