Execute Program

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'Pass Icon
  • 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'Pass Icon
  • 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'Pass Icon
  • Here's a code problem:

    Write a User class that:

    • Takes an initial name as a constructor argument. (We've provided this for you.)
    • Stores that in a names array property, representing a history of the user's names.
    • Has a name setter that adds a new name to the array using this.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']}Pass Icon
  • 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]Pass Icon
  • 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 error when 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 exceededPass Icon
  • In this case, the setter tried to do this.name = newName, which called the setter again, which did this.name = newName again, 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 name getter returned a property named this.actualName.