Object's property manipulation in Javascript

In Javascript, an object's property falls into exactly one of two categories (not both, not none of them): data property, accessor property (ECMA).

Definition

Regardless of with or without your explicit definition, all object's properties have their associated property descriptor, which has the following properties.

  • (both categories) configurable: specify whether the descriptor can be re-configured/changed or deleted.
  • (both categories) enumerable: enumerability of the property, which is used to determine the visibility of the property in some iteration methods/operators. For example: Array.prototype.length's descriptor's enumerable is false.
  • (data only) value: any value which represents the current value of the property.
  • (data only) writable: determine whether the value can be changed via the assignment operator or its equivalents (explained latter).
  • (accessor only) get: getter function (similar to the class's getter function).
  • (accessor only) set: setter function (similar to the class's setter function).

Because the property can be only data or accessor type, setting both [value or writable] and [get or set] is impossible and will throw a TypeError error.

A property's category is defined by the existences of [value or writable] and [get or set] in its associated descriptor.


Property/descriptor manipulation

These methods can be used to manipulate a property and its associated descriptor.

Object creation

Define or update

Note:

  • Properties defined by Object.defineProperty() / Reflect.defineProperty / Object.defineProperties() have enumerable be false by default,
    while Reflect.set() / accessor syntax have enumerable be true.
  • Object.defineProperty() / Object.defineProperties() throws an error if the operation fails or returns the passed object if success,
    while Reflect.defineProperty() returns a boolean to indicate the successfulness of the operation.
  • Object.assign() only copies enumerable and own properties from the source object.
    And, it does get value from the source object and set to the target object, without copying the associated descriptor.

Deletion

Both methods return a boolean to indicate the successfulness of the operation. They do manipulate only own properties.

Retrieval

In the following table, x slots mean any.

Methods / Operators Own key only? Enumerable only? String key only?
Object.entries() = Object.keys() + Object.values() yes yes yes
for ... in statement x yes yes
Object.prototype.propertyIsEnumerable() yes yes x
Reflect.ownKeys() = Object.getOwnPropertyNames() + Object.getOwnPropertySymbols() yes x x
Object.prototype.hasOwnProperty() yes x x
Object.getOwnPropertyDescriptor() / Reflect.getOwnPropertyDescriptor() yes x x
Object.getOwnPropertyDescriptors() yes x x
Reflect.has() / in operator x x x
Reflect.get() / property accessor syntax x x x

Note:

  • All these methods, if applicable, they do return arrays, not iterator objects (different from ES5 Map).
  • Before ES5, if non-object is passed to above Object's static methods (not Object.prototype's methods), the value is coerced to an object first. From ES5, they throw a TypeError in such a case.
  • On the other hand, Reflect's static methods always throw a TypeError if a non-object is passed in.

Note:

Other modifications

Methods / Operators Prevent adding new? Prevent changing [[prototype]]? Mark non-configurable and non-deletable? Mark values constant?
Object.freeze() / Object.isFrozen() yes yes yes yes
Object.seal() / Object.isSealed() yes yes yes no(*)
Object.preventExtensions() / Object.isExtensible() / Reflect.preventExtensions() / Reflect.isExtensible() yes yes no no(*)

Note:

  • (*) Because [[prototype]] is sealed, the internal prototype (e.g., __proto__) is marked constant. Other properties follow the specs.
  • Intuitively, extendability is a subset of seal, seal is a subset of freeze.
  • Object.preventExtensions() returns the passed in object, while Reflect.preventExtensions() return a boolean indicate the successfulness of the operation.
  • Non-empty ArrayBufferView cannot be frozen.
  • There is no way to rollback these operations.
  • All operations are applied with own property only, the prototype's properties remain untouched.