Les fonctions ont donc des méthodes, c’est Inception, mais t’inquiète, tu vas survivre, et haut la main en plus !

La première méthode est la bien-nommée call(). Elle permet d’appeler une fonction en lui passant des arguments bien définis, donc exactement comme si on l’appelait directement avec l’opérateur parenthèses, avec une petite différence critique : on précise d’abord, en premier, le this qu’on veut utiliser !

Quand on y réfléchit, c’est un peu normal d’avoir ça : puisque JS ne définit pas this tout seul autrement qu’en Sujet-Verbe-Complément, on doit logiquement avoir un mécanisme pour préciser this comme ça nous arrange.

Du coup par exemple, pour rester simples, quand JS voit elodie.sayHi('Anna'), il implémente notre règle d’or en faisant en fait elodie.sayHi.call(elodie, 'Anna'). La sémantique est exactement la même.

C’est hyper puissant quand on y pense : ça veut dire qu’on peut aller chercher n’importe quelle fonction, d’où qu’elle vienne, et l’appeler avec n’importe quel this ! Ça ouvre de sacrés opportunités, et d’ailleurs de nombreux aspects des bibliothèques et API les plus populaires seraient tout simplement impossibles à mettre en œuvre sans ça. Sans call(), point de React ou jQuery, les ami·e·s ! On en serait encore à faire clignoter des textes déroulants en Comic Sans sur Geocities, moi je vous le dis.

(Tu n’as pas la référence Geocities ? Tu as bien de la chance, alors que moi je suis déjà vieux.)

Cependant call() a une part d’ombre : appelée sur une fonction fléchée, elle échoue silencieusement. Ça n’explose pas, hein, note bien : mais comme une fonction fléchée ne peut pas, par définition, avoir son propre this, comme on dit dans notre jargon métier : call() pisse dans un violon.

Voyons ça en pratique.

(Exemple 13-call.js)

Ce code est conçu pour s’exécuter dans Node.js, du coup le this en vigueur au niveau « global » du module est en fait l’objet d’export du module CommonJS représenté par ce fichier. C’est la même chose que l’identifiant exports que nous fournit le contrat CommonJS.  Tiens, si ça se trouve, tu viens d’apprendre un truc, là aussi.

On a ici une petite fonction sayHi() déclarée traditionnellement : elle peut donc avoir ses propres this et arguments. Donc si je fais sayHi.call(aurore, 'Elliott'), ça devrait rouler nickel.

En revanche, la fonction fléchée greet(), dont la signature et le code sont pourtant identiques, n’est pas censée avoir cet environnement d’appel dédié, ça va donc partir en cacahuète, notre histoire…

Voyons ça, (démo). Aaaaah, en effet, pour sayHi() le this est bien recalé sur aurore, et arguments[0] vaut bien whom, donc 'Elliott'. Tout roule. Mais pour greet(), qu’est-ce donc est-ce que c’est donc ?! Le this est manifestement autre chose, puisque this.name a donné undefined (de fait, l’objet d’export n’a pas de propriété name). Quant à arguments[0], il correspond en fait au premier argument de la fonction d’enrobage CommonJS autour de mon module, donc à l’objet d’export, d’où la propriété zomg et sa valeur 'OH NOES'.

Pas glop, hein, call() sur fonctions fléchées ? Même pas un avertissement rien, paye ton bug chelou. Eh oui, c’est pas toujours le bon choix, une fonction fléchée.

Bon, quand même, call() c’est bien pratique. Le seul truc, c’est qu’il faut savoir exactement combien d’arguments on doit passer, parce qu’ils sont fournis individuellement à l’appel. C’est vrai qu’en ES2015 on pourrait spreader un tableau d’arguments, du coup ce serait plus flexible, mais jusqu’à ES5, et donc sur IE, on ne pouvait pas, alors que faire pour être un peu dynamique en terme d’arité ?