Skip to content
Lesson 14 of 15

Step 1 of 7 · Reading · ~3 min

Learn

Collections and Property Descriptors

Property Descriptors and Getters/Setters

Every property of an object carries a small record of settings: whether it can be changed, listed or deleted. By default you never see it, but Object.freeze, getters, setters and class accessors are all built on it. By the end of this lesson you can read and set those settings, write computed properties, and choose between freeze, seal and #private fields.

The descriptor

javascript

A data property has a value plus three flags:

  • writable: whether the value can be reassigned.
  • enumerable: whether the property appears in Object.keys, for...in, spread ({...obj}) and JSON.stringify.
  • configurable: whether the property can be deleted or its flags changed.

Properties made by assignment or in an object literal have all three set to true.

Object.defineProperty

Object.defineProperty(obj, name, descriptor) adds or changes a property with exactly the flags you give. Flags you leave out default to false, the opposite of normal assignment:

javascript

Without "use strict" those last two lines fail silently, which is worse: nothing changes and nothing tells you. Classes and ES modules are always strict.

Accessor properties: getters and setters

Instead of a stored value, a property can have a get function and/or a set function. Reading runs the getter; assigning runs the setter. From outside it looks like a normal property.

javascript

The same is available in literals (get gross() {...}) and classes. A setter is the natural place to validate:

javascript

A #name field is private by syntax: code outside the class body cannot even mention it, unlike an _underscore convention that anyone can ignore.

freeze, seal, preventExtensions

CallAdd propertiesDeleteChange values
Object.preventExtensions(o)noyesyes
Object.seal(o)nonoyes
Object.freeze(o)nonono

Object.isFrozen(o), isSealed and isExtensible report the state. All three are shallow: Object.freeze({ nested: { n: 1 } }) still lets you set nested.n. And they cannot be undone. A spread copy of a frozen object is a normal, unfrozen object, because flags are not copied.

Common mistakes

  • Expecting defineProperty to behave like assignment. Missing flags are false.
  • Assuming a frozen object is deeply immutable.
  • Forgetting that failed writes are silent outside strict mode.
  • Giving an accessor both a value and a get: it is one kind or the other.
  • Looking for a class's getters in JSON.stringify or Object.keys. They live on the prototype and are skipped.

Your exercise

Combine all of it in one function: copy some data, add a hidden read-only property and a computed one, then lock the result. The starter then probes every flag from the outside.

Up nextModules in Depth: Dynamic ImportsCollections and Property Descriptors

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…