Dynamically select a method of an object in JavaScript

By

Learn how to dynamically call a method on a JavaScript object by picking its name with a ternary and the square bracket notation, then invoking it with ().

~~~

To dynamically select a method of an object, use square brackets with an expression that evaluates to the method name, then invoke the result with parentheses.

Sometimes you have an object and you need to call one method or another, depending on some condition.

For example you have a car object and you either want to drive() it or to park() it, depending on the driver.sleepy value.

If the driver has a sleepy level over 6, we need to park the car before they fall asleep while driving.

Here is how you achieve this with an if/else condition:

if (driver.sleepy > 6) {
  car.park()
} else {
  car.drive()
}

This works fine. Let’s rewrite it to be more dynamic.

How does bracket notation work?

In JavaScript, car.park and car['park'] access the same property. The difference is what goes inside the brackets: any expression. JavaScript evaluates it, and the result is used as the property name.

Methods are properties too. Their value happens to be a function.

So we can use the ternary operator to choose the method name as a string, and select it from the object with square brackets:

car[driver.sleepy > 6 ? 'park' : 'drive']

With the above statement we get the method reference. We can directly invoke it by appending the parentheses:

car[driver.sleepy > 6 ? 'park' : 'drive']()

One line replaces the whole if/else block. You can pass arguments as usual, inside the parentheses.

This technique gets more useful when the method name comes from data, like a user action or an API response:

const action = 'drive'
car[action]()

What if the method does not exist?

If the name you compute is not a method of the object, the lookup returns undefined, and calling it throws:

const car = {
  drive() {
    console.log('driving')
  }
}

car['park']()
// TypeError: car.park is not a function

When the name comes from external data, guard the call:

if (typeof car[action] === 'function') {
  car[action]()
}

Alternatively, you can use optional chaining, which skips the call when the method is undefined:

car[action]?.()

Be careful with this

Since we invoke the method directly on the object, this inside the method still points to car.

But if you store the method reference in a variable first and call it later, that link is gone:

const move = car[action]
move()
// `this` no longer points to `car`

If you need to pass the method around, bind it first:

const move = car[action].bind(car)

Now move() behaves exactly like car[action]().

~~~

Related posts about js: