In der JavaScript-Welt gibt es Zwang - eine Detektivgeschichte
Nathan, du hast keine Ahnung, was du entdeckt hast.
Ich habe das jetzt seit Wochen untersucht. Alles begann in einer stürmischen Nacht im letzten Oktober. Ich bin versehentlich auf die NumberKlasse gestoßen - ich meine, warum in aller Welt hatte JavaScript eine NumberKlasse?
Ich war nicht auf das vorbereitet, was ich als nächstes herausfinden würde.
Es stellt sich heraus, dass JavaScript, ohne es Ihnen mitzuteilen, Ihre Zahlen in Objekte und Ihre Objekte in Zahlen direkt unter Ihrer Nase geändert hat.
JavaScript hatte gehofft, niemand würde es verstehen, aber die Leute haben seltsames unerwartetes Verhalten gemeldet, und jetzt habe ich dank Ihnen und Ihrer Frage die Beweise, die ich brauche, um dieses Ding weit aufzublasen.
Das haben wir bisher herausgefunden. Ich weiß nicht, ob ich Ihnen das überhaupt sagen soll - vielleicht möchten Sie Ihr JavaScript ausschalten.
> function dis() { return this }
undefined
Als Sie diese Funktion erstellt haben, hatten Sie wahrscheinlich keine Ahnung, was als nächstes passieren würde. Alles sah gut aus und alles war gut - für jetzt.
Keine Fehlermeldungen, nur das Wort "undefiniert" in der Konsolenausgabe, genau das, was Sie erwarten würden. Immerhin war dies eine Funktionsdeklaration - sie soll nichts zurückgeben.
Dies war jedoch nur der Anfang. Was als nächstes geschah, konnte niemand vorhersagen.
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
Ja, ich weiß, du hast ein erwartet 5, aber das hast du nicht, war es - du hast etwas anderes - etwas anderes.
Mir geht es genauso.
Ich wusste nicht, was ich davon halten sollte. Es hat mich verrückt gemacht. Ich konnte nicht schlafen, ich konnte nicht essen, ich versuchte es wegzutrinken, aber keine Menge Gebirgstau würde mich vergessen lassen. Es ergab einfach keinen Sinn!
Da fand ich heraus, was wirklich los war - es war Zwang, und es geschah genau dort vor meinen Augen, aber ich war zu blind, um es zu sehen.
Mozilla versuchte es zu begraben, indem sie es dort platzierte, wo sie wussten, dass niemand hinschauen würde - in ihrer Dokumentation .
Nach Stunden des rekursiven Lesens und erneuten Lesens und erneuten Lesens fand ich Folgendes:
"... und primitive Werte werden in Objekte konvertiert."
Es war genau dort klar, wie in Open Sans-Schrift geschrieben werden kann. Es war die call()Funktion - wie konnte ich so dumm sein?!
Meine Nummer war überhaupt keine Nummer mehr. In dem Moment, als ich es weitergab call(), wurde es etwas anderes. Es wurde ... ein Objekt.
Ich konnte es zuerst nicht glauben. Wie könnte das wahr sein? Aber ich konnte die Beweise, die sich um mich herum häuften, nicht ignorieren. Es ist genau dort, wenn Sie nur schauen:
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"
wtfhatte Recht. Zahlen können keine benutzerdefinierten Eigenschaften haben - das wissen wir alle! Es ist das erste, was sie dir an der Akademie beibringen.
Wir hätten es wissen müssen, als wir die Konsolenausgabe sahen - dies war nicht die Zahl, die wir dachten. Dies war ein Betrüger - ein Gegenstand, der sich als unsere süße unschuldige Zahl ausgibt.
Das war ... new Number(5).
Natürlich! Es machte vollkommen Sinn. call()hatte einen Job zu erledigen, er musste eine Funktion aufrufen, und um das zu tun, musste er füllen this, er wusste, dass er das nicht mit einer Nummer machen konnte - er brauchte ein Objekt und er war bereit, alles zu tun, um es zu bekommen, sogar wenn das bedeutete, unsere Nummer zu zwingen. Als er call()die Nummer 5sah, sah er eine Gelegenheit.
Es war der perfekte Plan: Warten Sie, bis niemand mehr hinschaut, und tauschen Sie unsere Nummer gegen ein Objekt aus, das genauso aussieht. Wir bekommen eine Nummer, die Funktion wird aufgerufen und niemand wäre klüger.
Es war wirklich der perfekte Plan, aber wie bei allen Plänen, auch bei den perfekten, war ein Loch darin, und wir wollten gleich hineinfallen.
Sehen Sie, was call()nicht verstanden wurde, war, dass er nicht der einzige in der Stadt war, der Zahlen erzwingen konnte. Das war schließlich JavaScript - Zwang war überall.
call() Ich nahm meine Nummer und wollte nicht aufhören, bis ich die Maske von seinem kleinen Betrüger abzog und ihn der gesamten Stack Overflow-Community aussetzte.
Aber wie? Ich brauchte einen Plan. Sicher, es sieht aus wie eine Zahl, aber ich weiß, dass es nicht so ist. Es muss einen Weg geben, das zu beweisen. Das ist es! Es sieht aus wie eine Zahl, aber kann es sich wie eine verhalten?
Ich sagte, fiveich brauche ihn, um fünfmal größer zu werden - er fragte nicht warum und ich erklärte es nicht. Ich habe dann getan, was jeder gute Programmierer tun würde: Ich habe multipliziert. Sicherlich konnte er sich hier nicht vortäuschen.
> five * 5
25
> five.wtf
'potato'
Verdammt! Nicht nur gut fivemultiplizieren wtfwar noch da. Verdammt dieser Typ und seine Kartoffel.
Was zur Hölle war los? War ich in dieser ganzen Sache falsch? Ist das fivewirklich eine Nummer? Nein, ich muss etwas vermissen, ich weiß es, es gibt etwas, das ich vergessen muss, etwas so Einfaches und Grundlegendes, dass ich es völlig übersehen habe.
Das sah nicht gut aus, ich hatte diese Antwort stundenlang geschrieben und war immer noch nicht näher dran, meinen Standpunkt zu vertreten. Ich konnte nicht so weitermachen, irgendwann hörten die Leute auf zu lesen, ich musste an etwas denken und ich musste schnell daran denken.
Warten Sie, das ist es! fivewar nicht 25, 25 war das Ergebnis, 25 war eine ganz andere Zahl. Natürlich, wie könnte ich das vergessen? Zahlen sind unveränderlich. Wenn Sie multiplizieren, wird 5 * 5nichts zugewiesen. Sie erstellen einfach eine neue Zahl25 .
Das muss hier passieren. Irgendwie , wenn ich mehrfach five * 5, fivemuss immer in eine Reihe gezwungen und diese Zahl muss der eine für die Multiplikation verwendet werden. Es sind die Ergebnisse dieser Multiplikation, die auf der Konsole gedruckt werden, nicht der Wert von sich fiveselbst. fivebekommt nie etwas zugewiesen - also ändert es sich natürlich nicht.
Wie kann ich mir dann fivedas Ergebnis einer Operation zuordnen? Ich habe es verstanden. Bevor fiveich überhaupt nachdenken konnte, schrie ich "++".
> five++
5
Aha! Ich hatte ihn! Jeder weiß , 5 + 1ist 6, war dies der Beweis Ich musste, dass er fivekeine Zahl war! Es war ein Betrüger! Ein schlechter Betrüger, der nicht zählen konnte. Und ich könnte es beweisen. So verhält sich eine reelle Zahl:
> num = 5
5
> num++
5
Warten? Was war hier los? seufz Ich war so in das Busting verwickelt, fivedass ich vergesse, wie Postbetreiber arbeiten. Wenn ich ++am Ende von fivesage, gebe ich den aktuellen Wert zurück und erhöhe ihn dann five. Es ist der Wert vor dem Vorgang, der auf der Konsole gedruckt wird. numwar in der Tat 6und ich konnte es beweisen:
>num
6
Zeit zu sehen, was fivewirklich war:
>five
6
... es war genau das, was es sein sollte. fivewar gut - aber ich war besser. Wenn fivees immer noch ein Objekt wäre, würde das bedeuten, dass es immer noch das Eigentum hätte wtfund ich wäre bereit, alles zu wetten, was es nicht tat.
> five.wtf
undefined
Aha! Ich lag richtig. Ich hatte ihn! fivewar jetzt eine Nummer - es war kein Objekt mehr. Ich wusste, dass der Multiplikationstrick es diesmal nicht retten würde. Sehen five++ist wirklich five = five + 1. Im Gegensatz zur Multiplikation weist der ++Operator einen Wert zu five. Insbesondere weist es ihm die Ergebnisse zu, von five + 1denen genau wie im Fall der Multiplikation eine neue unveränderliche Zahl zurückgegeben wird .
Ich wusste, dass ich ihn hatte und nur um sicherzugehen, dass er sich nicht herauswinden konnte. Ich hatte noch einen Test im Ärmel. Wenn ich recht hätte und fivejetzt wirklich eine Nummer wäre, würde das nicht funktionieren:
> five.wtf = 'potato?'
'potato?'
Diesmal würde er mich nicht täuschen. Ich wusste potato?, dass es auf der Konsole gedruckt werden würde, da dies die Ausgabe der Aufgabe ist. Die eigentliche Frage ist, wird es wtfnoch da sein?
> five.wtf
undefined
Genau wie ich vermutet habe - nichts - weil Nummern keine Eigenschaften zugewiesen werden können. Das haben wir im ersten Jahr an der Akademie gelernt;)
Danke Nathan. Dank Ihres Mutes, diese Frage zu stellen, kann ich das alles endlich hinter mich bringen und zu einem neuen Fall übergehen.
Wie dieser über die Funktion toValue(). Oh mein Gott. Nein!
++scheint aber den zugrunde liegenden Typ zu beeinflussen