liveBook · Manning

Setter

As explained in Secrets of the JavaScript Ninja, Second Edition

A property accessor function called when a value is assigned to the property.

A setter lets an assignment do more than store a value. When code assigns to a property with a setter, JavaScript implicitly calls its setter function with the assigned value. The caller still writes an ordinary-looking assignment, such as `collection.firstNinja = "Hachi"`. section 8.1.1

Defining a setter

  • In an object literal or ES6 class, prefix the property name with `set`: `set firstNinja(value) { this.ninjas[0] = value; }`. A matching `get` accessor can make the same property readable. section 8.1.1
  • Use `Object.defineProperty` to supply a `set` function in a property descriptor. An accessor descriptor cannot combine `set` or `get` with `value` and `writable`. section 8.1.1 section 7.3.1
Listing 8.2. Defining getters and setters in object literals
const ninjaCollection = {
  ninjas: ["Yoshi", "Kuma", "Hattori"],
  get firstNinja(){                              #A
    report("Getting firstNinja");                #A
    return this.ninjas[0];                       #A
  },                                             #A
  set firstNinja(value){                         #B 
    report("Setting firstNinja");                #B
    this.ninjas[0] = value;                      #B
  }                                              #B
};

assert(ninjaCollection.firstNinja === "Yoshi",   #C
       "Yoshi is the first ninja");              #C

ninjaCollection.firstNinja = "Hachi";            #D

assert(ninjaCollection.firstNinja === "Hachi"    #E
    && ninjaCollection.ninjas[0] ===  "Hachi",   #E
       "Now Hachi is the first ninja");          #E

Follow the assignment to `firstNinja`: its setter updates the first array element, and the getter makes that change visible through property access. section 8.1.1

Validation is a practical reason to intercept writes. A `skillLevel` setter can check `Number.isInteger(value)` before updating a variable held in a closure. An integer succeeds; a string triggers a `TypeError` and leaves the stored value unchanged. The check adds overhead, so use it where guarding the property is worth the cost. Setters can also log writes, track value history, or notify code of changes. section 8.1.2

Listing 8.5. Validating property value assignments with setters
  function Ninja() {
    let _skillLevel = 0;

    Object.defineProperty(this, 'skillLevel', {
      get: () => _skillLevel,
      set: value => {
        if(!Number.isInteger(value)){                               #A
          throw new TypeError("Skill level should be a number");    #A
        }                                                           #A
        _skillLevel = value;
      }
    });
  }

  const ninja = new Ninja();

  ninja.skillLevel = 10;                                       #B
  assert(ninja.skillLevel === 10, "The value was updated");    #B

  try {                                                        #C
    ninja.skillLevel = "Great";                                #C
    fail("Should not be here");                                #C
  } catch(e){                                                  #C
    pass("Setting a non-integer value throws an exception");   #C
  }                                                            #C

Compare the accepted integer assignment with the rejected string assignment; the setter checks the value before changing the stored variable. section 8.1.2

Writing a computed property

A `fullTitle` getter can join `name` and `clan` when read. Its setter can split an assigned title and update those two underlying properties. The apparent field stores no separate title value; assignment changes the state from which the title is computed. section 8.1.3

An ordinary function can also be used as a setter in the broader sense: a `style(element, name, value)` helper sets a DOM style when given a value and reads it when that argument is omitted. Unlike a property accessor, callers invoke this helper explicitly. section 12.3.2 section 8.1.1

How Secrets of the JavaScript Ninja, Second Edition covers it

How deep it goes
Works through accessor syntax, implicit calls, descriptor definitions, and validation; mentions logging and change notification without implementing them.
Examples
Uses an array-backed first-element property, integer-only skill levels, and a title assembled from two properties.
What it assumes
Assumes familiarity with JavaScript objects, functions, and property assignment while introducing accessor syntax through examples.
What you can do after
Build it from scratch with an object literal, ES6 class, or Object.defineProperty, then validate assignments or update component properties.
How it connects
Connects setters to getters, property descriptors, closures, computed properties, and proxies.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Secrets of the JavaScript Ninja, Second Edition. The section links point to the parts of the book it draws on.