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
