Execute Program

Modern JavaScript: Computed Methods and Accessors

Welcome to the Computed Methods and Accessors 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 computed properties, where the key of an object is determined dynamically.

  • >
    const loginCounts = {
    ['Be' + 'tty']: 7
    };
    loginCounts.Betty;
    Result:
    7Pass Icon
  • This feature exists on classes as well. We can create methods, static methods, accessors, and static accessors, all with computed names. Those names can be built up using any JavaScript expression.

  • >
    let methodName = 'getName';

    class User {
    constructor(name) {
    this.name = name;
    }

    [methodName]() {
    return this.name;
    }
    }

    new User('Amir').getName();
    Result:
    'Amir'Pass Icon
  • >
    class User {
    constructor(name) {
    this.name = name;
    }

    get ['user' + 'Name']() {
    return this.name;
    }
    }

    new User('Betty').userName;
    Result:
    'Betty'Pass Icon
  • >
    class User {
    constructor(name) {
    this.name = name;
    }

    static [['get', 'User', 'Name'].join('')](user) {
    return user.name;
    }
    }

    User.getUserName(new User('Cindy'));
    Result:
    'Cindy'Pass Icon
  • What if we define the class, then change the value that was used to compute a method name? For example, if a method name comes from a variable, we might give that variable a new value.

  • That won't have any effect: computed method names are only evaluated once, when the class is defined.

  • (In the next example, remember that accessing an object property that doesn't exist will return undefined.)

  • >
    let methodName = 'userName';

    class User {
    constructor(name) {
    this.name = name;
    }

    get [methodName]() {
    return this.name;
    }
    }

    methodName = 'theUserName';

    new User('Amir').theUserName;
    Result:
    undefinedPass Icon
  • When the User class above is defined, the value of methodName is 'userName', The getter references this variable, so the class is defined with a get userName() accessor. When the value of methodName changes, the getter's name isn't affected because it was already defined.

  • If the class is created dynamically inside a function, we can use the function's arguments to name the class's accessors and methods. This is consistent with what we've seen before: classes inside function have access to variables that are in scope within that function.

  • >
    function createUserClass(makeItVerbose) {
    let methodName;
    if (makeItVerbose) {
    methodName = 'theNameOfTheUser';
    } else {
    methodName = 'userName';
    }

    return class {
    constructor(name) {
    this.name = name;
    }

    [methodName]() {
    return this.name;
    }
    };
    }

    const TerseUser = createUserClass(false);
    const VerboseUser = createUserClass(true);

    [
    new TerseUser('Amir').userName(),
    new VerboseUser('Betty').theNameOfTheUser(),
    ];
    Result:
    ['Amir', 'Betty']Pass Icon
  • Here's a code problem:

    Write a function named getRecipe. It should take one argument: dish, and return a class with a method whose computed method name comes from dish. For example, if dish is 'pasta', then the class should have a pasta method. The method should return `a recipe for ${dish}`.

    function getRecipe(dish) {
    return class {
    [dish]() {
    return `a recipe for ${dish}`;
    }
    };
    }
    const recipes = [
    new (getRecipe('pasta'))().pasta(),
    new (getRecipe('cake'))().cake(),
    ];
    recipes;
    Goal:
    ['a recipe for pasta', 'a recipe for cake']
    Yours:
    ['a recipe for pasta', 'a recipe for cake']Pass Icon
  • Computed method names can be confusing because they break one of our assumptions: "I can look at the class to see what methods it has". For that reason, it's good to use them sparingly.

  • However, you will encounter computed methods in real-world code, especially in library or framework code that needs to be very generic. When you do encounter them, remember this rule: when the class is being constructed, the virtual machine evaluates the string to get the method or accessor name. After the class is defined, those names won't change.