Eine Liste lustiger und kniffliger JavaScript-Beispiele
JavaScript ist eine großartige Sprache. Es verfügt über eine einfache Syntax, ein großes Ökosystem und, was am wichtigsten ist, eine großartige Community.
Gleichzeitig wissen wir alle, dass JavaScript eine ziemlich lustige Sprache mit kniffligen Teilen ist. Manche davon können unseren Arbeitsalltag schnell zur Hölle machen, andere können uns zum Lachen bringen.
Die ursprüngliche Idee für WTFJS stammt von Brian Leroux. Diese Liste ist stark von seinem Vortrag „WTFJS“ auf der dotJS 2012 inspiriert:
Sie können dieses Handbuch mit npm
installieren. Führen Sie einfach Folgendes aus:
$ npm install -g wtfjs
Sie sollten jetzt in der Lage sein, wtfjs
in der Befehlszeile auszuführen. Dadurch wird das Handbuch in Ihrem ausgewählten $PAGER
geöffnet. Ansonsten können Sie hier weiterlesen.
Die Quelle ist hier verfügbar: https://github.com/denysdovhan/wtfjs
Derzeit gibt es diese Übersetzungen von wtfjs :
Helfen Sie beim Übersetzen in Ihre Sprache
Hinweis: Übersetzungen werden von ihren Übersetzern gepflegt. Sie enthalten möglicherweise nicht alle Beispiele und vorhandene Beispiele sind möglicherweise veraltet.
[]
ist gleich ![]
true
ist nicht equal ![]
, aber auch nicht equal []
NaN
ist kein NaN
Object.is()
und ===
seltsame Fälle[]
ist wahr, aber nicht true
null
ist falsch, aber nicht false
document.all
ist ein Objekt, aber es ist undefiniertundefined
und Number
parseInt
ist ein Bösewichttrue
und false
NaN
ist[]
und null
sind Objekte0.1 + 0.2
String
constructor
__proto__
`${{Object}}`
try..catch
arguments
und PfeilfunktionenNumber.toFixed()
zeigt verschiedene Zahlen anMath.max()
kleiner als Math.min()
null
mit 0
{}{}
ist undefiniertarguments
verbindlichalert
aus der HöllesetTimeout
Objekttrue
ZahlNur zum Spaß
– „Just for Fun: Die Geschichte eines zufälligen Revolutionärs“ , Linus Torvalds
Das Hauptziel dieser Liste besteht darin, einige verrückte Beispiele zu sammeln und nach Möglichkeit zu erklären, wie sie funktionieren. Einfach weil es Spaß macht, etwas zu lernen, was wir vorher nicht wussten.
Wenn Sie ein Anfänger sind, können Sie diese Hinweise verwenden, um tiefer in JavaScript einzutauchen. Ich hoffe, dass diese Hinweise Sie dazu motivieren, mehr Zeit mit der Lektüre der Spezifikation zu verbringen.
Wenn Sie ein professioneller Entwickler sind, können Sie diese Beispiele als großartige Referenz für alle Eigenheiten und unerwarteten Kanten unseres geliebten JavaScript betrachten.
Lesen Sie dies auf jeden Fall einfach durch. Sie werden wahrscheinlich etwas Neues finden.
️ Hinweis: Wenn Ihnen die Lektüre dieses Dokuments Spaß macht, denken Sie bitte darüber nach, den Autor dieser Sammlung zu unterstützen.
// ->
wird verwendet, um das Ergebnis eines Ausdrucks anzuzeigen. Zum Beispiel:
1 + 1 ; // -> 2
// >
bedeutet das Ergebnis von console.log
oder einer anderen Ausgabe. Zum Beispiel:
console . log ( "hello, world!" ) ; // > hello, world!
//
ist nur ein Kommentar, der der Erklärung dient. Beispiel:
// Assigning a function to foo constant
const foo = function ( ) { } ;
[]
ist gleich ![]
Array ist gleich, nicht Array:
[ ] == ! [ ] ; // -> true
Der abstrakte Gleichheitsoperator wandelt beide Seiten in Zahlen um, um sie zu vergleichen, und beide Seiten werden aus unterschiedlichen Gründen zur Zahl 0
. Arrays sind wahrheitsgemäß, daher ist rechts das Gegenteil eines wahrheitsgemäßen Werts false
, der dann auf 0
gezwungen wird. Auf der linken Seite wird jedoch ein leeres Array in eine Zahl umgewandelt, ohne zuerst ein boolescher Wert zu werden, und leere Arrays werden in 0
umgewandelt, obwohl sie wahr sind.
So vereinfacht sich dieser Ausdruck:
+ [ ] == + ! [ ] ;
0 == + false ;
0 == 0 ;
true ;
Siehe auch []
ist wahr, aber nicht true
.
!
)true
ist nicht equal ![]
, aber auch nicht equal []
Array ist nicht gleich true
, aber auch nicht Array ist nicht gleich true
; Array ist gleich false
, nicht Array ist auch gleich false
:
true == [ ] ; // -> false
true == ! [ ] ; // -> false
false == [ ] ; // -> true
false == ! [ ] ; // -> true
true == [ ] ; // -> false
true == ! [ ] ; // -> false
// According to the specification
true == [ ] ; // -> false
toNumber ( true ) ; // -> 1
toNumber ( [ ] ) ; // -> 0
1 == 0 ; // -> false
true == ! [ ] ; // -> false
! [ ] ; // -> false
true == false ; // -> false
false == [ ] ; // -> true
false == ! [ ] ; // -> true
// According to the specification
false == [ ] ; // -> true
toNumber ( false ) ; // -> 0
toNumber ( [ ] ) ; // -> 0
0 == 0 ; // -> true
false == ! [ ] ; // -> true
! [ ] ; // -> false
false == false ; // -> true
! ! "false" == ! ! "true" ; // -> true
! ! "false" === ! ! "true" ; // -> true
Betrachten Sie Folgendes Schritt für Schritt:
// true is 'truthy' and represented by value 1 (number), 'true' in string form is NaN.
true == "true" ; // -> false
false == "false" ; // -> false
// 'false' is not the empty string, so it's a truthy value
! ! "false" ; // -> true
! ! "true" ; // -> true
"b" + "a" + + "a" + "a" ; // -> 'baNaNa'
Dies ist ein altmodischer Witz in JavaScript, aber überarbeitet. Hier ist das Original:
"foo" + + "bar" ; // -> 'fooNaN'
Der Ausdruck wird als 'foo' + (+'bar')
ausgewertet, wodurch 'bar'
in keine Zahl umgewandelt wird.
+
)NaN
ist kein NaN
NaN === NaN ; // -> false
Die Spezifikation definiert streng die Logik hinter diesem Verhalten:
- Wenn sich
Type(x)
vonType(y)
unterscheidet, wird false zurückgegeben.- Wenn
Type(x)
Zahl ist, dann
- Wenn
x
NaN ist, wird false zurückgegeben.- Wenn
y
NaN ist, wird false zurückgegeben.- … … …
— 7.2.14 Strikter Gleichheitsvergleich
Gemäß der Definition von NaN
des IEEE:
Vier sich gegenseitig ausschließende Beziehungen sind möglich: kleiner als, gleich, größer als und ungeordnet. Der letzte Fall tritt auf, wenn mindestens ein Operand NaN ist. Jedes NaN soll ungeordnet mit allem verglichen werden, einschließlich sich selbst.
– „Was ist der Grund dafür, dass alle Vergleiche für IEEE754-NaN-Werte falsch zurückgeben?“ bei StackOverflow
Object.is()
und ===
seltsame Fälle Object.is()
bestimmt, ob zwei Werte den gleichen Wert haben oder nicht. Es funktioniert ähnlich wie der ===
Operator, es gibt jedoch einige seltsame Fälle:
Object . is ( NaN , NaN ) ; // -> true
NaN === NaN ; // -> false
Object . is ( - 0 , 0 ) ; // -> false
- 0 === 0 ; // -> true
Object . is ( NaN , 0 / 0 ) ; // -> true
NaN === 0 / 0 ; // -> false
Im JavaScript-Jargon sind NaN
und NaN
zwar dieselben Werte, aber nicht unbedingt gleich. NaN === NaN
falsch ist, hat offenbar historische Gründe, daher wäre es wahrscheinlich besser, es so zu akzeptieren, wie es ist.
Ebenso sind -0
und 0
genau gleich, haben aber nicht den gleichen Wert.
Weitere Einzelheiten zu NaN === NaN
finden Sie im obigen Fall.
Sie würden es nicht glauben, aber …
( ! [ ] + [ ] ) [ + [ ] ] +
( ! [ ] + [ ] ) [ + ! + [ ] ] +
( [ ! [ ] ] + [ ] [ [ ] ] ) [ + ! + [ ] + [ + [ ] ] ] +
( ! [ ] + [ ] ) [ ! + [ ] + ! + [ ] ] ;
// -> 'fail'
Wenn wir diese Masse an Symbolen in Stücke zerlegen, stellen wir fest, dass das folgende Muster häufig auftritt:
! [ ] + [ ] ; // -> 'false'
! [ ] ; // -> false
Also versuchen wir, []
zu false
hinzuzufügen. Aber aufgrund einer Reihe interner Funktionsaufrufe ( binary + Operator
-> ToPrimitive
-> [[DefaultValue]]
) konvertieren wir am Ende den richtigen Operanden in einen String:
! [ ] + [ ] . toString ( ) ; // 'false'
Wenn wir uns einen String als Array vorstellen, können wir über [0]
auf sein erstes Zeichen zugreifen:
"false" [ 0 ] ; // -> 'f'
Der Rest ist offensichtlich, aber das i
ist knifflig. Das i
in fail
wird erfasst, indem die Zeichenfolge 'falseundefined'
generiert und das Element im Index ['10']
erfasst wird.
Weitere Beispiele:
+ ! [ ] // -> 0
+ ! ! [ ] // -> 1
! ! [ ] // -> true
! [ ] // -> false
[ ] [ [ ] ] // -> undefined
+ ! ! [ ] / + ! [ ] // -> Infinity
[ ] + { } // -> "[object Object]"
+ { } // -> NaN
[]
ist wahr, aber nicht true
Ein Array ist ein wahrer Wert, der jedoch nicht gleich true
ist.
! ! [ ] // -> true
[ ] == true // -> false
Hier finden Sie Links zu den entsprechenden Abschnitten in der ECMA-262-Spezifikation:
!
)null
ist falsch, aber nicht false
Obwohl null
ein falscher Wert ist, ist er nicht gleich false
.
! ! null ; // -> false
null == false ; // -> false
Gleichzeitig sind andere falsche Werte wie 0
oder ''
gleich false
.
0 == false ; // -> true
"" == false ; // -> true
Die Erklärung ist dieselbe wie für das vorherige Beispiel. Hier ist der entsprechende Link:
document.all
ist ein Objekt, aber es ist undefiniert
️ Dies ist Teil der Browser-API und funktioniert nicht in einer Node.js-Umgebung️
Obwohl document.all
ein Array-ähnliches Objekt ist und Zugriff auf die DOM-Knoten auf der Seite ermöglicht, reagiert es auf die Funktion typeof
als undefined
.
document . all instanceof Object ; // -> true
typeof document . all ; // -> 'undefined'
Gleichzeitig ist document.all
nicht gleich undefined
.
document . all === undefined ; // -> false
document . all === null ; // -> false
Aber gleichzeitig:
document . all == null ; // -> true
document.all
war früher eine Möglichkeit, auf DOM-Elemente zuzugreifen, insbesondere mit alten Versionen des IE. Obwohl es nie ein Standard war, wurde es im alten JS-Code häufig verwendet. Als der Standard mit neuen APIs (wiedocument.getElementById
) weiterentwickelt wurde, wurde dieser API-Aufruf obsolet und das Standardkomitee musste entscheiden, was damit geschehen sollte. Aufgrund der weiten Verbreitung entschied man sich, die API beizubehalten, jedoch einen vorsätzlichen Verstoß gegen die JavaScript-Spezifikation einzuführen. Der Grund dafür, dass bei Verwendung des Strict-Gleichheitsvergleichs „false
und bei Verwendung desundefined
Gleichheitsvergleichs“true
und bei Verwendung des Abstract Equality-Vergleichs „false“ zurückgegeben wird, liegt in der vorsätzlichen Verletzung der Spezifikation, die dies ausdrücklich zulässt.— „Veraltete Funktionen – document.all“ bei WhatWG – HTML-Spezifikation – „Kapitel 4 – ToBoolean – Falsche Werte“ bei YDKJS – Typen und Grammatik
Number.MIN_VALUE
ist die kleinste Zahl, die größer als Null ist:
Number . MIN_VALUE > 0 ; // -> true
Number.MIN_VALUE
ist5e-324
, also die kleinste positive Zahl, die mit Float-Genauigkeit dargestellt werden kann, also so nahe wie möglich an Null herankommt. Es definiert die beste Auflösung, die Floats Ihnen bieten können.Der insgesamt kleinste Wert ist nun
Number.NEGATIVE_INFINITY
obwohl er im engeren Sinne nicht wirklich numerisch ist.— „Warum ist
0
kleiner alsNumber.MIN_VALUE
in JavaScript?“ bei StackOverflow
️ Ein Fehler in V8 v5.5 oder niedriger (Node.js <=7)️
Ihr wisst alle über das lästige undefined is not a function Bescheid , aber was ist damit?
// Declare a class which extends null
class Foo extends null { }
// -> [Function: Foo]
new Foo ( ) instanceof null ;
// > TypeError: function is not a function
// > at … … …
Dies ist nicht Teil der Spezifikation. Es handelt sich lediglich um einen Fehler, der inzwischen behoben wurde, sodass es in Zukunft kein Problem mehr damit geben sollte.
Es ist eine Fortsetzung der Geschichte mit dem vorherigen Fehler in einer modernen Umgebung (getestet mit Chrome 71 und Node.js v11.8.0).
class Foo extends null { }
new Foo ( ) instanceof null ;
// > TypeError: Super constructor null of Foo is not a constructor
Dies ist kein Fehler, weil:
Object . getPrototypeOf ( Foo . prototype ) ; // -> null
Wenn die Klasse keinen Konstruktor hat, erfolgt der Aufruf aus der Prototypenkette. Aber im übergeordneten Element gibt es keinen Konstruktor. Für alle Fälle möchte ich klarstellen, dass null
ein Objekt ist:
typeof null === "object" ;
Daher können Sie davon erben (obwohl ich in der Welt des OOP für solche Begriffe geschlagen worden wäre). Sie können den Nullkonstruktor also nicht aufrufen. Wenn Sie diesen Code ändern:
class Foo extends null {
constructor ( ) {
console . log ( "something" ) ;
}
}
Sie sehen den Fehler:
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
Und wenn man super
hinzufügt:
class Foo extends null {
constructor ( ) {
console . log ( 111 ) ;
super ( ) ;
}
}
JS gibt einen Fehler aus:
TypeError: Super constructor null of Foo is not a constructor
Was passiert, wenn Sie versuchen, zwei Arrays hinzuzufügen?
[ 1 , 2 , 3 ] + [ 4 , 5 , 6 ] ; // -> '1,2,34,5,6'
Die Verkettung geschieht. Schritt für Schritt sieht es so aus:
[ 1 , 2 , 3 ] +
[ 4 , 5 , 6 ] [
// call toString()
( 1 , 2 , 3 )
] . toString ( ) +
[ 4 , 5 , 6 ] . toString ( ) ;
// concatenation
"1,2,3" + "4,5,6" ;
// ->
( "1,2,34,5,6" ) ;
Sie haben ein Array mit 4 leeren Elementen erstellt. Trotz allem erhalten Sie aufgrund der abschließenden Kommas ein Array mit drei Elementen:
let a = [ , , , ] ;
a . length ; // -> 3
a . toString ( ) ; // -> ',,'
Nachgestellte Kommas (manchmal auch „Abschlusskommas“ genannt) können beim Hinzufügen neuer Elemente, Parameter oder Eigenschaften zum JavaScript-Code nützlich sein. Wenn Sie eine neue Eigenschaft hinzufügen möchten, können Sie einfach eine neue Zeile hinzufügen, ohne die zuvor letzte Zeile zu ändern, wenn diese Zeile bereits ein abschließendes Komma verwendet. Dadurch werden Versionskontrollunterschiede sauberer und das Bearbeiten von Code ist möglicherweise weniger mühsam.
– Nachgestellte Kommas bei MDN
Array-Gleichheit ist in JS ein Monster, wie Sie unten sehen können:
[ ] == '' // -> true
[ ] == 0 // -> true
[ '' ] == '' // -> true
[ 0 ] == 0 // -> true
[ 0 ] == '' // -> false
[ '' ] == 0 // -> true
[ null ] == '' // true
[ null ] == 0 // true
[ undefined ] == '' // true
[ undefined ] == 0 // true
[ [ ] ] == 0 // true
[ [ ] ] == '' // true
[ [ [ [ [ [ ] ] ] ] ] ] == '' // true
[ [ [ [ [ [ ] ] ] ] ] ] == 0 // true
[ [ [ [ [ [ null ] ] ] ] ] ] == 0 // true
[ [ [ [ [ [ null ] ] ] ] ] ] == '' // true
[ [ [ [ [ [ undefined ] ] ] ] ] ] == 0 // true
[ [ [ [ [ [ undefined ] ] ] ] ] ] == '' // true
Auf die oben genannten Beispiele sollten Sie sehr genau achten! Das Verhalten wird im Abschnitt 7.2.15 Abstrakter Gleichheitsvergleich der Spezifikation beschrieben.
undefined
und Number
Wenn wir keine Argumente an den Number
Konstruktor übergeben, erhalten wir 0
. Der Wert undefined
wird formalen Argumenten zugewiesen, wenn keine tatsächlichen Argumente vorhanden sind. Sie können also davon ausgehen, dass Number
ohne Argumente“ undefined
als Wert seines Parameters annimmt. Wenn wir jedoch undefined
übergeben, erhalten wir NaN
.
Number ( ) ; // -> 0
Number ( undefined ) ; // -> NaN
Laut Spezifikation:
n
+0
.n
sein? ToNumber(value)
.undefined
sollte ToNumber(undefined)
NaN
zurückgeben.Hier ist der entsprechende Abschnitt:
argument
) parseInt
ist ein Bösewicht parseInt
ist berühmt für seine Macken:
parseInt ( "f*ck" ) ; // -> NaN
parseInt ( "f*ck" , 16 ) ; // -> 15
Erläuterung: Dies geschieht, weil parseInt
weiterhin Zeichen für Zeichen analysiert, bis es auf ein Zeichen trifft, das es nicht kennt. Das f
in 'f*ck'
ist die hexadezimale Ziffer 15
.
Das Parsen Infinity
in Integer ist etwas ...
//
parseInt ( "Infinity" , 10 ) ; // -> NaN
// ...
parseInt ( "Infinity" , 18 ) ; // -> NaN...
parseInt ( "Infinity" , 19 ) ; // -> 18
// ...
parseInt ( "Infinity" , 23 ) ; // -> 18...
parseInt ( "Infinity" , 24 ) ; // -> 151176378
// ...
parseInt ( "Infinity" , 29 ) ; // -> 385849803
parseInt ( "Infinity" , 30 ) ; // -> 13693557269
// ...
parseInt ( "Infinity" , 34 ) ; // -> 28872273981
parseInt ( "Infinity" , 35 ) ; // -> 1201203301724
parseInt ( "Infinity" , 36 ) ; // -> 1461559270678...
parseInt ( "Infinity" , 37 ) ; // -> NaN
Seien Sie auch beim Parsen null
vorsichtig:
parseInt ( null , 24 ) ; // -> 23
Erläuterung:
Es wandelt
null
in die Zeichenfolge"null"
um und versucht, diese umzuwandeln. Für die Radixe 0 bis 23 gibt es keine Ziffern, die konvertiert werden können, daher wird NaN zurückgegeben. Bei 24 wird"n"
, der 14. Buchstabe, zum Zahlensystem hinzugefügt. Bei 31 wird"u"
, der 21. Buchstabe, hinzugefügt und die gesamte Zeichenfolge kann dekodiert werden. Ab 37 kann kein gültiger Zahlensatz mehr generiert werden und es wirdNaN
zurückgegeben.— „parseInt(null, 24) === 23… warte, was?“ bei StackOverflow
Vergessen Sie nicht die Oktalzahlen:
parseInt ( "06"