You probably know about computed keys in objects, but did you realize you can use computed keys with the method shorthand? You probably don't ever want to do this, but you can.
const methodName = 'myMethod';
const computedKey = 'computed';
const myObj = {
// Computed Property
[computedKey]: 'It worked!',
// 🤔 Is this a good idea? Probably not, no.
// Computed property + Method shorthand
[methodName]() {
return this.computed;
},
};
myObj.myMethod();
// 'It worked!'
History Lesson
In the earliest versions of JavaScript, functions were all defined in the global scope. Even back then you could use brackets to get or set computed values, but most of the capabilities we think of weren't available.
ECMAScript 3
ECMAScript 3 brought us function expressions and object methods. You could use bracket notation to set properties or methods.
// Old JavaScript.
var computedKey = 'computed';
var myObj = {
// Key and function separately.
myMethod: function () {
return this.computed;
},
};
myObj[computedKey] = 'It worked!';
myObj.myMethod();
// 'It worked!'
It was possible to have dynamic method names if you wanted but they had to be defined after the object was created.
myObj = {};
myObj[methodName] = function() {
return this.computed;
};
ECMAScript 2015
ECMAScript 2015 introduced both object method shorthand and computed properties.
// Exciting new JavaScript!
const computedKey = 'computed';
const myObj = {
// Method shorthand
myMethod() {
return this.computed;
},
// Computed Property
[computedKey]: 'It worked!',
};
myObj.myMethod();
// 'It worked!'
Even though the MDN article doesn't specifically mention it, you can mix method shorthand with computed property names, as shown at the beginning of the article.
Problems
There might be some edge cases where it makes sense to do this, but in general we should avoid this technique. It makes it very difficult to locate methods when trying to understand the code, and reduces the effectiveness of code editor support like IntelliSense and type information.
Alternatives
Hashes or proxies can be good alternatives to computed method names. Take a look at some of the ways we could make this work and let me know which ones you think would work best for you!
Common Code
const methodName = 'myMethod';
const computedKey = 'computed';
const myObj = {
getComputed() {
return this.computed;
},
[computedKey]: 'It worked!',
};
Plain Mapping
A simple map of strings to match up method names requires very little setup but makes it a little harder to call the methods.
const methodMap = {
[methodName]: 'getComputed',
};
myObj[methodMap.myMethod]();
// 'It worked!';
Bound Mapping
Using an object with methods bound to the original object requires more setup but simplifies the code for the consumer.
const methodMapBound = {
[methodName]: myObj.getComputed.bind(myObj),
};
methodMapBound.myMethod();
// 'It worked!'
Basic Proxy
A Proxy object eliminates most of the complication, as you can interact directly with the proxy. This uses a static check in the getter to find our computed property.
const basicProxy = new Proxy(myObj, {
get(target, prop, receiver) {
if (prop === methodName) {
return myObj.getComputed;
}
return Reflect.get(...arguments);
},
});
basicProxy.myMethod();
// 'It worked!'
Proxy Plus Map
Using a property name map a in the Plain Mapping example, we can support any number of method mappings. If we are willing to accept a little risk of infinite loops, we can actually support multiple indirection!
const methodMap = {
[methodName]: 'getComputed',
'thisIsWild': methodName,
};
const methodProxy = new Proxy(myObj, {
get(target, prop, receiver) {
if (methodMap[prop]) {
// Using receiver allows multiple indirection
return receiver[methodMap[prop]];
}
return Reflect.get(...arguments);
},
});
methodProxy.myMethod();
// 'It worked!'
methodProxy.thisIsWild();
// 'It worked!'
Conclusion
I hope you enjoyed this brief wander down some unexpected and (hopefully) unused capabilities of JavaScript!
Top comments (0)