Propriétés et méthodes concises en JS
Par Christophe Porteneuve • Publié le 23 décembre 2021 • 3 min

This page is also available in English.

Voici le premier article de notre nouvelle série, JS idiomatique. Aujourd’hui, on se penche sur les propriétés et méthodes concises, qui nous permettent de déclarer de façon plus courte, plus claire et plus efficace les membres d’un littéral objet ou d’une classe.

Vous préférez une vidéo ?

Si vous êtes du genre à préférer regarder que lire pour apprendre, on a pensé à vous :

La redondance répétitive

Voici un petit exemple de littéraux objet JS « à la papa »…

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
)
}

Ça ne vous sort pas par les yeux, ces body: body, method: method, headers: headers et autres name: name ?!

Et pourtant, bon sang, dans du JS courant ça arrive tout le temps qu’on construise ainsi des littéraux objets ayant, pour tout ou partie de leurs propriétés, une valeur obtenue en référençant un identifiant homonyme, présent dans la portée. En somme, a: a.

Propriétés concises

ES2015 (l’édition 2015 d’ECMAScript, le standard officiel qui sous-tend JavaScript) a introduit les syntaxes concises dans les littéraux objets (et les corps de classes, mais c’est une autre histoire).

Le cas qui nous occupe (a: a) est tellement courant qu’il dispose d’une syntaxe rien que pour lui : on ne met l’identifiant qu’une fois. Voyez plutôt :

function makeAPICall(url, method, body, headers) {
// Un appel bien simplifié
return fetch(url, { body, method, headers }).then((res) => res.json())
}

function createUser(name) {
// …

// Là aussi, le 3e argument est un peu plus concis
return makeAPICall('/api/v1/users', 'POST', { name }).then(
handler.success,
handler.failure
)
}

C’est beaucoup mieux, non ? Remarquez qu’en pratique, on décide de recourir à cette syntaxe propriété par propriété : il est fréquent que nos littéraux objets aient aussi des propriétés qui ne correspondent pas à ce besoin :

// Deux propriétés concises, une propriété classique.
return { name, age: yearSince(birthDay), gender }

Méthodes concises

Dans le même esprit, la façon « traditionnelle » de déclarer des « méthodes », à savoir :

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

// Ou pour nos amis jQuerystes…
$.getJSON('/api/v1/users', {
onSuccess: function (res) {
/* … */
},
})

…a du plomb dans l’aile. Il faut avouer qu’elle désarçonne les personnes venant d’autres langages, la syntaxe n’ayant rien à voir. C’est dû au fait qu’en JavaScript, il n’y a pas de « méthode » au sens répandu du terme (en particulier, les fonctions que vous définissez ne sont pas intrinsèquement associées à des objets), juste des propriétés dont les valeurs s’avèrent être des fonctions.

La syntaxe ci-dessus a plusieurs inconvénients, petits ou grands :

  • C’est déroutant quand on vient d’un langage à la POO (programmation orientée objets) classique
  • C’est verbeux
  • Avant ES2015, ça donnait des fonctions anonymes (faute de nom explicite entre le mot-clé function et le début de la signature)
  • Si on commençait à jouer avec le prototype de notre objet et à tenter d’accéder au prototype parent depuis une telle fonction, on pouvait avoir des surprises.

ES2015 a donc permis les méthodes concises, qui donneraient ceci :

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

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

Elle a plusieurs avantages :

  • Ça ressemble davantage aux langages avec une POO classique
  • C’est plus court (on a donc plus de chances que les devs s’en servent, en jouant sur la flemme à la frappe)
  • Les fonctions sont forcément nommées (même si en pratique, ES2015 aurait nommé automatiquement les fonctions affectées aux propriétés avec la syntaxe précédente)
  • La chaîne de prototypes fonctionne correctement au sein de ces fonctions

En somme, on n’a que des raisons de s’en servir !

Une ligne sur deux

En pratique, ce genre de code se rencontre extrêmement souvent dans du code JS idiomatique. Pour les propriétés concises notamment, on pourrait presque parler d’une ligne sur deux. Voici quelques exemples réels issus de nos formations ou de nos projets en production :

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

// Et puis…

function selectState({
// Ça, c’est de la déstructuration (on en parle bientôt)
config: { canPromptForNotify },
goals,
today,
todaysProgress,
}) {
// Ça, ce sont des propriétés concises
return {
canPromptForNotify,
goals,
today,
todaysProgress,
}
}

// Mais aussi…

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

// Tant qu'on y est…

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

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

// Allez, une dernière parmi moult :

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

Ça vous a plu ? Ne manquez pas la suite !

Pour être sûr·e de ne rater aucun de nos tutos et articles, le mieux est encore de vous abonner à notre newsletter et à notre chaîne YouTube. Vous pouvez aussi nous suivre sur Twitter.

Et bien entendu, n’hésitez pas à jeter un œil à nos formations ! Si explorer l’intégralité des recoins du langage vous intéresse, la ES Total notamment est faite pour vous !