Modern JavaScript: Accessor Properties on Classes
Welcome to the Accessor Properties on Classes lesson!
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
We've seen accessors (getters and setters) in object literals:
>
const user = {get name() { return 'Be' + 'tty'; }};user.name;Result:
'Betty'
We can also use accessors in classes. As with object literals, a getter's function will be called when we access the property.
>
class User {constructor(name) {this.actualName = name;}get name() {return `${this.actualName} the user`;}}new User('Betty').name;Result:
'Betty the user'
Setters work in classes too.
>
class User {constructor(name) {this.actualName = name;}set name(newName) {this.actualName = newName;}}const user = new User('Amir');user.name = 'Betty';user.actualName;Result:
'Betty'
Here's a code problem:
Write a
Userclass that:- Takes an initial
nameas a constructor argument. (We've provided this for you.) - Stores that in a
namesarray property, representing a history of the user's names. - Has a
namesetter that adds a new name to the array usingthis.names.push(...).
class User {constructor(name) {this.names = [name];}set name(newName) {this.names.push(newName);}}const user = new User('Amir');const names1 = user.names.slice();user.name = 'Betty';const names2 = user.names.slice();({names1, names2});- Goal:
{names1: ['Amir'], names2: ['Amir', 'Betty']}- Yours:
{names1: ['Amir'], names2: ['Amir', 'Betty']}
- Takes an initial
When we extend a class, the child inherits any getters and setters from the parent.
>
class Animal {constructor(legCount) {this.legCount = legCount;}get canWalk() {return this.legCount > 0;}}class Worm extends Animal {constructor() {super(0);}}class Dog extends Animal {constructor() {super(4);}}[new Worm().canWalk, new Dog().canWalk];Result:
[false, true]
One final note about accessors. Ultimately, the accessor is just another property on the instance object.
This can lead to a confusing type of bug when an accessor tries to set a property with the same name as the accessor. It can cause many different problems depending on the exact details. Here's an example where it blows the stack, throwing a
RangeError.(You can type
errorwhen a code example will throw an error.)>
class User {set name(newName) {this.name = newName;}}const amir = new User();amir.name = 'Amir';Result:
RangeError: Maximum call stack size exceeded
In this case, the setter tried to do
this.name = newName, which called the setter again, which didthis.name = newNameagain, which called the setter again, etc. That continued until we hit the maximum stack size, which caused the JavaScript runtime to error.The solution here is to store the data in a property whose name doesn't match the setter or getter's name. For example, in some examples above, the
namegetter returned a property namedthis.actualName.