Il y a de la coercition dans le monde JavaScript - Une histoire de détective
Nathan, tu n'as aucune idée de ce que tu as découvert.
Cela fait des semaines que j'enquête sur ce sujet. Tout a commencé par une nuit d'orage en octobre dernier. Je suis tombé par hasard sur le Number
Je veux dire, pourquoi diable JavaScript avait-il une classe de Number
classe ?
Je n'étais pas préparé à ce que j'allais découvrir ensuite.
Il s'avère que JavaScript, sans vous le dire, a transformé vos nombres en objets et vos objets en nombres sous votre nez.
JavaScript espérait que personne ne s'en apercevrait, mais des gens ont signalé des comportements étranges et inattendus, et maintenant, grâce à vous et à votre question, j'ai les preuves dont j'ai besoin pour faire éclater cette affaire au grand jour.
Voici ce que nous avons découvert jusqu'à présent. Je ne sais pas si je devrais même vous dire cela - vous devriez peut-être désactiver votre JavaScript.
> function dis() { return this }
undefined
Lorsque vous avez créé cette fonction, vous n'aviez probablement aucune idée de ce qui allait se passer ensuite. Tout avait l'air bien, et tout allait bien - pour l'instant.
Aucun message d'erreur, juste le mot "undefined" dans la sortie de la console, exactement ce à quoi on pouvait s'attendre. Après tout, il s'agit d'une déclaration de fonction, qui n'est pas censée renvoyer quoi que ce soit.
Mais ce n'était que le début. Personne n'aurait pu prédire ce qui s'est passé ensuite.
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
Oui, je sais, vous vous attendiez à une 5
Mais ce n'est pas ce que vous avez obtenu, n'est-ce pas ? Vous avez obtenu quelque chose d'autre, quelque chose de différent.
La même chose m'est arrivée.
Je ne savais pas quoi en penser. Cela m'a rendu fou. Je ne pouvais pas dormir, je ne pouvais pas manger, j'essayais de boire, mais aucune quantité de Mountain Dew ne pouvait me faire oublier. Cela n'avait aucun sens !
C'est à ce moment-là que j'ai découvert ce qui se passait réellement - il s'agissait de coercition, et cela se passait juste devant mes yeux, mais j'étais trop aveugle pour le voir.
Mozilla a essayé de l'enterrer en le plaçant là où ils savaient que personne ne le regarderait - leur site web. la documentation .
Après des heures de lecture récursive, de relecture et de relecture, j'ai trouvé ceci :
"... et les valeurs primitives seront converties en objets."
C'était là, en toutes lettres, en police Open Sans. C'était le call()
fonction - comment ai-je pu être aussi stupide ?
Mon numéro n'était plus du tout un numéro. Dès que je l'ai transmis à call()
il est devenu autre chose. Il est devenu... un objet.
Je n'arrivais pas à y croire au début. Comment cela pouvait-il être vrai ? Mais je ne pouvais pas ignorer les preuves qui s'accumulaient autour de moi. Il suffit de regarder pour s'en rendre compte :
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"
wtf
avait raison. Les numéros ne peuvent pas avoir de propriétés personnalisées - nous le savons tous ! C'est la première chose que l'on apprend à l'académie.
Nous aurions dû le savoir dès que nous avons vu la sortie de la console - ce n'était pas le nombre que nous pensions qu'il était. Il s'agissait d'un imposteur, d'un objet qui se faisait passer pour notre doux et innocent numéro.
C'était... new Number(5)
.
Bien sûr ! C'est tout à fait logique. call()
avait une tâche à accomplir, il devait invoquer une fonction, et pour ce faire, il devait remplir le formulaire this
Il savait qu'il ne pouvait pas faire cela avec un numéro - il avait besoin d'un objet et il était prêt à faire n'importe quoi pour l'obtenir, même si cela signifiait contraindre notre numéro. Quand call()
a vu le nombre 5
Il y a vu une opportunité.
C'était le plan parfait : attendre que personne ne regarde et échanger notre numéro contre un objet qui lui ressemble. Nous obtenons un numéro, la fonction est invoquée et personne ne s'en aperçoit.
C'était vraiment le plan parfait, mais comme tous les plans, même les plans parfaits, il y avait un trou dans le plan, et nous étions sur le point de tomber dedans.
Voir, ce que call()
ne comprenait pas qu'il n'était pas le seul en ville à pouvoir contraindre les chiffres. Après tout, il s'agissait de JavaScript - la coercition était omniprésente.
call()
a pris mon numéro, et je n'allais pas m'arrêter avant d'avoir arraché le masque de son petit imposteur et de l'avoir exposé à l'ensemble de la communauté Stack Overflow.
Mais comment ? J'avais besoin d'un plan. Bien sûr, cela ressemble à un chiffre, mais je sais que ce n'en est pas un, il doit y avoir un moyen de le prouver. C'est ça ! C'est ça regards comme un nombre, mais peut-il agir comme tel ?
J'ai dit five
J'ai besoin qu'il devienne 5 fois plus grand - il n'a pas demandé pourquoi et je n'ai pas expliqué. J'ai alors fait ce que tout bon programmeur ferait : J'ai multiplié. Il n'y avait aucune chance qu'il puisse faire semblant de s'en sortir.
> five * 5
25
> five.wtf
'potato'
Bon sang ! Non seulement five
se multiplient très bien wtf
était toujours là. Maudit soit ce type et sa pomme de terre.
Qu'est-ce qui se passe ? Est-ce que je me suis trompé ? Est-ce que five
vraiment un numéro ? Non, il doit me manquer quelque chose, je le sais, il y a quelque chose que je dois oublier, quelque chose de si simple et de si basique que je l'ignore complètement.
J'écrivais cette réponse depuis des heures et je n'étais toujours pas parvenu à faire valoir mon point de vue. Je ne pouvais pas continuer ainsi, les gens finiraient par arrêter de lire, il fallait que je trouve quelque chose et vite.
Attendez, c'est ça ! five
n'était pas 25, 25 était le résultat, 25 était un nombre complètement différent. Bien sûr, comment pourrais-je oublier ? Les nombres sont immuables. Lorsque vous multipliez 5 * 5
rien n'est attribué à quoi que ce soit vous créez simplement un nouveau numéro 25
.
C'est sûrement ce qui se passe ici. D'une manière ou d'une autre, lorsque je multiplie five * 5
, five
doit être transformé en un nombre et ce nombre doit être celui utilisé pour la multiplication. C'est le résultat de cette multiplication qui est imprimé sur la console, et non la valeur de five
même. five
ne se voit jamais attribuer quoi que ce soit - il ne change donc évidemment pas.
Comment puis-je obtenir five
de s'attribuer le résultat d'une opération. J'ai compris. Avant five
J'ai eu le temps de réfléchir et j'ai crié "++".
> five++
5
Aha ! Je l'ai eu ! Tout le monde le sait 5 + 1
es 6
C'était la preuve dont j'avais besoin pour mettre en évidence le fait que le five
n'était pas un numéro ! C'était un imposteur ! Un mauvais imposteur qui ne savait pas compter. Et je pouvais le prouver. Voici comment se comporte un vrai nombre :
> num = 5
5
> num++
5
Attendez, qu'est-ce qui se passe ici ? soupir J'ai été tellement pris par l'envie d'en découdre five
que j'oublie le fonctionnement des opérateurs postaux. Lorsque j'utilise l'opérateur ++
à la fin de five
Je dis qu'il faut renvoyer la valeur actuelle, puis l'incrémenter five
. C'est la valeur avant l'opération se produit et est imprimée sur la console. num
était en fait 6
et je pourrais le prouver :
>num
6
Il est temps de voir ce que five
était vraiment :
>five
6
...c'était exactement ce qu'il fallait. five
était bon - mais j'étais meilleur. Si five
était toujours un objet, cela signifierait qu'il aurait toujours la propriété wtf
et j'étais prêt à parier tout ce qu'il n'a pas fait.
> five.wtf
undefined
J'avais raison. Je l'avais ! five
était un numéro - ce n'était plus un objet. Je savais que l'astuce de la multiplication ne le sauverait pas cette fois-ci. Voir five++
est vraiment five = five + 1
. Contrairement à la multiplication, la ++
L'opérateur attribue une valeur à five
. Plus précisément, il lui attribue les résultats de five + 1
qui, comme dans le cas de la multiplication, renvoie une nouvelle valeur immuable de nombre .
Je savais que je le tenais, et je voulais m'assurer qu'il ne pourrait pas se tortiller pour s'en sortir. J'avais un autre test dans ma manche. Si j'avais raison, et five
était vraiment un nombre, cela ne fonctionnerait pas :
> five.wtf = 'potato?'
'potato?'
Il n'allait pas me tromper cette fois-ci. Je savais que potato?
allait être imprimée sur la console parce que c'est la sortie de l'affectation. La vraie question est de savoir si wtf
toujours là ?
> five.wtf
undefined
Comme je m'en doutais, il n'y a rien, car les numéros ne peuvent pas être attribués à des propriétés. Nous avons appris cela la première année à l'académie ;)
Merci Nathan. Grâce au courage dont vous avez fait preuve en posant cette question, je peux enfin mettre tout cela derrière moi et passer à une nouvelle affaire.
Comme celle concernant la fonction toValue()
. Oh, mon Dieu. Noooon !