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() and Object.setPrototypeOf().
  • .[[Extensible]]: boolean
    • Indicates if it is possible to add properties to an object.
    • Can be set to false via Object.preventExtensions().
  • .[[PrivateFieldValues]]: EntryList

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 attribute set.

Additionally, there are attributes that both kinds of properties have. The following table lists all attributes and their default values.

Kind of propertyName and type of attributeDefault value
Data propertyvalue: anyundefined
writable: booleanfalse
Accessor propertyget: (this: any) => anyundefined
set: (this: any, v: any) => voidundefined
All propertiesconfigurable: booleanfalse
enumerable: booleanfalse

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 is false, 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 from true to false. The rationale behind this anomaly is historical: Property .length of Arrays has always been writable and non-configurable. Allowing its writable attribute to be changed enables us to freeze Arrays.
  • enumerable influences some operations (such as Object.keys()). If it is false, 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:

  1. const proto = {
  2. prop: 1,
  3. };
  4. // Make proto.prop non-writable:
  5. Object.defineProperty(
  6. proto, 'prop', {writable: false});
  7. const obj = Object.create(proto);
  8. assert.throws(
  9. () => obj.prop = 2,
  10. /^TypeError: Cannot assign to read only property 'prop'/);

For more information, see [content not included].