Execute Program

Modern JavaScript: Accessors in Object Literals

Welcome to the Accessors in Object Literals lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • JavaScript has always had object literals:

  • >
    const user = {
    userName: 'Amir'
    };
    user.userName;
    Result:
    'Amir'Pass Icon
  • With the object syntax above, we can only create objects with fixed properties. To change a property's value, we have to modify the object.

  • But a property doesn't have to hold a fixed value, like 'Amir'. It can also hold a function. To access it, we add a function call to the property, like user.getAge().

  • >
    const user = {
    getAge: function() { return 'I am ' + 22; }
    };
    user.getAge();
    Result:
    'I am 22'Pass Icon
  • Modern JavaScript streamlines this syntax with the get keyword. To use it, we declare the object property as a function starting with get. When we access the property with the usual user.age syntax, the function is automatically called and we get its return value. This kind of property is called a "getter".

  • >
    const user = {
    get age() { return 38; }
    };
    user.age;
    Result:
  • Because the function above returns a fixed string, it behaves like a regular property would. But the getter function doesn't have to return a constant value. It can return anything we want.

  • >
    const user = {
    get age() { return 'I am ' + 38; }
    };
    user.age;
    Result:
    'I am 38'Pass Icon
  • Here's a code problem:

    Add a userName getter to this object. It should return the value of the name variable.

    let name = 'Amir';
    const user = {
    get userName() { return name; },
    };
    const userName1 = user.userName;
    name = 'Betty';
    const userName2 = user.userName;
    [userName1, userName2];
    Goal:
    ['Amir', 'Betty']
    Yours:
    ['Amir', 'Betty']Pass Icon
  • The get properties above are called getters. As you might suspect, there's a complementary feature for writing to objects called setters.

  • Normally, writing to an object's key replaces the value at that key:

  • >
    const user = {userName: 'Amir'};
    user.userName = 'Betty';
    user.userName;
    Result:
    'Betty'Pass Icon
  • A setter property has an associated function, like a getter does. This time the function takes one argument: the value being written to the property.

  • Like with getter functions, we don't need to explicitly call the setter function or manually pass in the argument. When the value of a property is modified, JavaScript calls the setter function with the new value as the argument. The setter function can do anything it wants with that argument.

  • >
    const user = {
    realName: 'Amir',
    set userName(newName) { this.realName = newName; },
    };

    user.userName = 'Betty';
    user.realName;
    Result:
    'Betty'Pass Icon
  • >
    const user = {
    realName: 'Amir',
    set userName(newName) {
    this.realName = `New name: ${newName}`;
    },
    };

    user.userName = 'Betty';
    user.realName;
    Result:
    'New name: Betty'Pass Icon
  • If we try to read the value of a setter, we'll get undefined. The object has no value at that key, even though there is a setter for that key.

  • >
    const user = {
    realName: 'Amir',
    set userName(newName) { this.realName = newName; }
    };
    user.userName;
    Result:
    undefinedPass Icon
  • We can include both a getter and a setter in one object. You can think of this as defining two parts of a single property. One part handles reading from the property value and the other part handles writing to it.

  • >
    const user = {
    realName: 'Amir',
    get userName() { return this.realName; },
    set userName(newName) { this.realName = newName; },
    };
    user.userName = 'Betty';
    [user.realName, user.userName];
    Result:
    ['Betty', 'Betty']Pass Icon
  • We can use setters to track changes over time. Other code can modify our object properties as usual, using user.userName = someNewName. But behind the scenes, our user.userName setter can store a history of what happened.

  • Here's a code problem:

    Add a userName setter to this object. It should push userName to the names list. Inside of the setter function, you can push a value onto names with this.names.push(newName).

    const user = {
    names: ['Amir'],
    set userName(newName) {
    this.names.push(newName);
    }
    };
    user.userName = 'Betty';
    user.names;
    Goal:
    ['Amir', 'Betty']
    Yours:
    ['Amir', 'Betty']Pass Icon
  • Here's a more complete example. Once again, we use an array to store the history of the user's names. The user's current userName is the last userName in that array. We also wrap the object in a createUser function so we don't have to hard-code the initial userName.

  • >
    function createUser(userName) {
    return {
    names: [userName],
    get userName() { return this.names[this.names.length - 1]; },
    set userName(userName) { this.names.push(userName); }
    };
    }

    const user = createUser('Amir');
    user.userName = 'Betty';
    user.names;
    Result:
    ['Amir', 'Betty']Pass Icon
  • One more small detail. The get and set keywords are required when creating getters and setters. If an object's keys are regular functions, then they won't be automatically called when we read or write to a property. We'll get the old JavaScript behavior from before getters and setters even existed.

  • >
    const user = {
    userName: function () { return 'Amir'; }
    };
    typeof user.userName;
    Result: