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'senumerable
isfalse
. - (data only)
value
: any value which represents the current value of the property. - (data only)
writable
: determine whether thevalue
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
- Literal notation. Such as
{}
,function(){}
, ... new Object() (or new Object
), orReflect.construct(Object, [])
- Object.create()
- Object.fromEntries()
Define or update
- Object.defineProperty() / Reflect.defineProperty()
- Object.defineProperties()
- Object.assign()
- Reflect.set() (returns a boolean to indicate the successfulness of the operation)
- Property accessor syntax with the assignment operator and its relevance (+=, -=, /=, *=, >>=, >>>=, ||=, ??=, |=, --, ++, <<=, %=, &&=, ^=, &=, ...)
Note:
- Properties defined by
Object.defineProperty()
/Reflect.defineProperty
/Object.defineProperties()
haveenumerable
befalse
by default,
whileReflect.set()
/accessor syntax
haveenumerable
betrue
. Object.defineProperty()
/Object.defineProperties()
throws an error if the operation fails or returns the passed object if success,
whileReflect.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.
Prototype related
- Object.prototype.__proto__: highly discouraged in usage since this is not a standard ECMA feature.
- Object.prototype.constructor
- Object.getPrototypeOf() / Reflect.getPrototypeOf()
- Object.setPrototypeOf() / Reflect.setPrototypeOf()
- Object.prototype.isPrototypeOf() / instanceof operator
Note:
- Object.setPrototypeOf() returns the passed in object, while Reflect.setPrototypeOf() returns a boolean to indicate the successfulness of the operation.
a instanceof b
is equivalent toa.isPrototypeOf(b.prototype)
.
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.