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:
7
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'
>
class User {constructor(name) {this.name = name;}get ['user' + 'Name']() {return this.name;}}new User('Betty').userName;Result:
'Betty'
>
class User {constructor(name) {this.name = name;}static [['get', 'User', 'Name'].join('')](user) {return user.name;}}User.getUserName(new User('Cindy'));Result:
'Cindy'
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:
undefined
When the
Userclass above is defined, the value ofmethodNameis'userName', The getter references this variable, so the class is defined with aget userName()accessor. When the value ofmethodNamechanges, 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']
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 fromdish. For example, ifdishis'pasta', then the class should have apastamethod. 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']
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.