JS shorthand properties and methods
By Christophe Porteneuve • Published on 23 December 2021 • 3 min

Cette page est également disponible en français.

Here’s the first installment of our new series: Idiomatic JS. Today we look at shorthand properties and methods, that let us write object literals in a nicer, more concise way.

Repetitive duplication

Here’s a pretty common case of “old-school” JS object literals:

function makeAPICall(url, method, body, headers) {
return fetch(url, {
body: body,
method: method,
headers: headers,
}).then((res) => res.json())
}

function createUser(name) {
const handler = {
failure: function (error) {
/* … */
},
success: function (payload) {
/* … */
},
}

return makeAPICall('/api/v1/users', 'POST', { name: name }).then(
handler.success,
handler.failure
)
}

Doesn’t stuff like body: body, method: method, headers: headers or name: name make your eyes bleed eventually?!

And yet, dang it, this happens all the time in everyday JS: we want to build an object literal where all or most properties have values that are just references to same-name values in the scope. Basically, a: a.

Shorthand properties

ES2015 (the 2015 edition of ECMAScript, which is the official standard for JavaScript) introduced shorthand notations for object literals.

The case in point (a: a) is so common that it has its very own shorthand: just write the identifier once. Check this out:

function makeAPICall(url, method, body, headers) {
// This is so much simpler
return fetch(url, { body, method, headers }).then((res) => res.json())
}

function createUser(name) {
// …

// Here too, the third argument reads nicer.
return makeAPICall('/api/v1/users', 'POST', { name }).then(
handler.success,
handler.failure
)
}

Isn’t that way better? Also note you can opt to use this on a per-property basis: quite often, some of our object literal’s properties don’t fit that pattern, and that’s okay:

// Two shorthand properties, one classical property.
return { name, age: yearSince(birthDay), gender }

Shorthand methods

In the same spirit, the “traditional” way of declaring “methods” has been this:

const handler = {
failure: function (error) {
/* … */
},
success: function (payload) {
/* … */
},
}

// For our jQuerystans our there...
$.getJSON('/api/v1/users', {
onSuccess: function (res) {
/* … */
},
})

This has had a good run, but it’s time to move on. This syntax is indeed quite baffling to people coming to JS from other languages. This is due to JavaScript not having methods in the classical sense; in particular, functions are never intrinsically bound to objects, so “methods” are just properties whose values happen to be function references.

The syntax above has a few drawbacks, big or small:

  • It’s confusing when coming from another, classical-OOP language
  • It’s verbose
  • Before ES2015, this resulted in anonymous functions (for lack of an explicit name between the function keyword and the opening parens of the signature)
  • Should you try and mess around with our object’s prototype then try to access the parent prototype from such a function, you were sometimes in for a surprise.

ES2015 thus introduced shorthand methods, that look like this:

const handler = {
failure(error) {
/* … */
},
success(payload) {
/* … */
},
}

$.getJSON('/api/v1/users', {
onSuccess(res) {
/* … */
},
})

This is better in many ways:

  • It looks a lot more like what you see in popular, classical-OOP languages
  • It’s shorter (and thus has a better chance of being used by devs, betting on typing laziness)
  • Functions are intrinsically named (even though ES2015 auto-names any unnamed function except on-the-fly callbacks)
  • The prototype chain works as intended inside such functions

In short: there’s zero reason not to use it, go for it!

Every other line (of your code)

Such scenarios are extremely frequent in idiomatic JS. Shorthand properties, in particular, occur virtually every other line. Here are a few real-world examples from our training courses or production projects:

export function logIn(email, password) {
return { type: LOGIN, payload: { email, password } }
}

// Then…

function selectState({
// This is destructuring (we'll soon cover it extensively)
config: { canPromptForNotify },
goals,
today,
todaysProgress,
}) {
// These are shorthand properties
return {
canPromptForNotify,
goals,
today,
todaysProgress,
}
}

// And also…

const notif = new window.Notification(title, {
body: text,
icon,
lang: 'en',
requireInteraction,
tag: 'goal-tracker',
vibrate: [100, 50, 100, 50, 100],
})

// As long as we’re here…

async signUp({ email, firstName, lastName, password, roles }) {
const user = await this.create({
email,
firstName,
lastName,
password,
roles,
})

const token = getTokenForUser(user)
return { user, token }
}

// OK, a last one for the road (among so many):

function ackFeedbacks(event) {
// …
ackMutation.mutate({ feedbackIds: ackedIds, comment })
}

There’s always more!

Besides our wealth of other articles, you may wish to look at our live training courses! In particular, if you like super-deep dives into JavaScript itself, we heartily recommend our 360° ES course!