Daftar contoh JavaScript yang lucu dan rumit
JavaScript adalah bahasa yang hebat. Ia memiliki sintaksis yang sederhana, ekosistem yang besar dan, yang paling penting, komunitas yang hebat.
Pada saat yang sama, kita semua tahu bahwa JavaScript adalah bahasa yang cukup lucu dengan bagian-bagian yang rumit. Beberapa di antaranya dapat dengan cepat mengubah pekerjaan kita sehari-hari menjadi seperti neraka, dan beberapa di antaranya dapat membuat kita tertawa terbahak-bahak.
Ide awal WTFJS adalah milik Brian Leroux. Daftar ini sangat terinspirasi oleh ceramahnya “WTFJS” di dotJS 2012:
Anda dapat menginstal buku pegangan ini menggunakan npm
. Jalankan saja:
$ npm install -g wtfjs
Anda seharusnya bisa menjalankan wtfjs
di baris perintah sekarang. Ini akan membuka manual di $PAGER
yang Anda pilih. Jika tidak, Anda dapat melanjutkan membaca di sini.
Sumbernya tersedia di sini: https://github.com/denysdovhan/wtfjs
Saat ini, ada terjemahan wtfjs berikut ini:
Membantu menerjemahkan ke bahasa Anda
Catatan: Terjemahan dikelola oleh penerjemahnya. Mereka mungkin tidak memuat semua contoh, dan contoh-contoh yang ada mungkin sudah ketinggalan jaman.
[]
sama dengan ![]
true
tidak sama dengan ![]
, tetapi tidak sama dengan []
jugaNaN
bukan NaN
Object.is()
dan ===
kasus aneh[]
benar, tetapi tidak true
null
itu salah, tapi tidak false
document.all
adalah sebuah objek, tetapi tidak terdefinisiundefined
dan Number
parseInt
adalah orang jahattrue
dan false
NaN
adalah[]
dan null
adalah objek0.1 + 0.2
String
constructor
__proto__
`${{Object}}`
try..catch
yang berbahaya..tangkaparguments
dan fungsi panahNumber.toFixed()
menampilkan nomor yang berbedaMath.max()
kurang dari Math.min()
null
dengan 0
{}{}
tidak terdefinisiarguments
yang mengikatalert
dari nerakasetTimeout
true
Hanya untuk bersenang-senang
— “Hanya untuk Bersenang-senang: Kisah Seorang Revolusioner yang Tidak Disengaja” , Linus Torvalds
Tujuan utama dari daftar ini adalah untuk mengumpulkan beberapa contoh gila dan menjelaskan cara kerjanya, jika memungkinkan. Hanya karena menyenangkan mempelajari sesuatu yang belum kita ketahui sebelumnya.
Jika Anda seorang pemula, Anda dapat menggunakan catatan ini untuk mendalami JavaScript lebih dalam. Saya harap catatan ini akan memotivasi Anda untuk meluangkan lebih banyak waktu membaca spesifikasinya.
Jika Anda seorang pengembang profesional, Anda dapat menganggap contoh-contoh ini sebagai referensi bagus untuk semua keunikan dan sisi tak terduga dari JavaScript kesayangan kita.
Bagaimanapun, baca saja ini. Anda mungkin akan menemukan sesuatu yang baru.
️ Catatan: Jika Anda senang membaca dokumen ini, mohon pertimbangkan untuk mendukung penulis koleksi ini.
// ->
digunakan untuk menampilkan hasil ekspresi. Misalnya:
1 + 1 ; // -> 2
// >
berarti hasil dari console.log
atau keluaran lainnya. Misalnya:
console . log ( "hello, world!" ) ; // > hello, world!
//
hanyalah komentar yang digunakan untuk penjelasan. Contoh:
// Assigning a function to foo constant
const foo = function ( ) { } ;
[]
sama dengan ![]
Array sama dengan bukan array:
[ ] == ! [ ] ; // -> true
Operator persamaan abstrak mengubah kedua sisi menjadi angka untuk membandingkannya, dan kedua sisi menjadi angka 0
karena alasan yang berbeda. Array bersifat benar, jadi di sebelah kanan, kebalikan dari nilai kebenaran adalah false
, yang kemudian dipaksa menjadi 0
. Namun di sebelah kiri, array kosong dipaksa menjadi angka tanpa menjadi boolean terlebih dahulu, dan array kosong dipaksa menjadi 0
, meskipun sebenarnya.
Berikut adalah cara menyederhanakan ungkapan ini:
+ [ ] == + ! [ ] ;
0 == + false ;
0 == 0 ;
true ;
Lihat juga []
benar, tetapi tidak true
.
!
)true
tidak sama dengan ![]
, tetapi tidak sama dengan []
juga Array tidak sama dengan true
, tetapi array juga tidak sama dengan true
; Array sama dengan false
, bukan Array juga sama dengan 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
Pertimbangkan ini selangkah demi selangkah:
// 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'
Ini adalah lelucon jadul dalam JavaScript, tetapi dibuat ulang. Ini yang asli:
"foo" + + "bar" ; // -> 'fooNaN'
Ekspresi dievaluasi sebagai 'foo' + (+'bar')
, yang mengubah 'bar'
menjadi bukan angka.
+
)NaN
bukan NaN
NaN === NaN ; // -> false
Spesifikasi secara ketat mendefinisikan logika di balik perilaku ini:
- Jika
Type(x)
berbeda dariType(y)
, kembalikan false .- Jika
Type(x)
adalah Angka, maka
- Jika
x
adalah NaN , kembalikan false .- Jika
y
adalah NaN , kembalikan false .- … … …
— 7.2.14 Perbandingan Kesetaraan yang Ketat
Mengikuti definisi NaN
dari IEEE:
Ada empat relasi yang saling eksklusif: kurang dari, sama, lebih besar dari, dan tidak berurutan. Kasus terakhir muncul ketika setidaknya satu operan adalah NaN. Setiap NaN harus membandingkan yang tidak berurutan dengan segala sesuatu, termasuk dirinya sendiri.
— “Apa alasan semua perbandingan menghasilkan nilai palsu untuk nilai IEEE754 NaN?” di StackOverflow
Object.is()
dan ===
kasus aneh Object.is()
menentukan apakah dua nilai memiliki nilai yang sama atau tidak. Cara kerjanya mirip dengan operator ===
tetapi ada beberapa kasus aneh:
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
Dalam istilah JavaScript, NaN
dan NaN
memiliki nilai yang sama tetapi tidak sepenuhnya sama. NaN === NaN
salah tampaknya karena alasan historis jadi mungkin lebih baik menerimanya apa adanya.
Demikian pula, -0
dan 0
sama persis, namun nilainya tidak sama.
Untuk detail lebih lanjut tentang NaN === NaN
, lihat kasus di atas.
Anda tidak akan percaya, tapi…
( ! [ ] + [ ] ) [ + [ ] ] +
( ! [ ] + [ ] ) [ + ! + [ ] ] +
( [ ! [ ] ] + [ ] [ [ ] ] ) [ + ! + [ ] + [ + [ ] ] ] +
( ! [ ] + [ ] ) [ ! + [ ] + ! + [ ] ] ;
// -> 'fail'
Dengan memecah kumpulan simbol tersebut menjadi beberapa bagian, kita melihat bahwa pola berikut sering terjadi:
! [ ] + [ ] ; // -> 'false'
! [ ] ; // -> false
Jadi kami mencoba menambahkan []
ke false
. Namun karena sejumlah panggilan fungsi internal ( binary + Operator
-> ToPrimitive
-> [[DefaultValue]]
) kami akhirnya mengonversi operan kanan menjadi string:
! [ ] + [ ] . toString ( ) ; // 'false'
Memikirkan string sebagai array kita dapat mengakses karakter pertamanya melalui [0]
:
"false" [ 0 ] ; // -> 'f'
Selebihnya sudah jelas, tetapi huruf i
rumit. i
yang fail
diambil dengan menghasilkan string 'falseundefined'
dan mengambil elemen pada indeks ['10']
.
Contoh lainnya:
+ ! [ ] // -> 0
+ ! ! [ ] // -> 1
! ! [ ] // -> true
! [ ] // -> false
[ ] [ [ ] ] // -> undefined
+ ! ! [ ] / + ! [ ] // -> Infinity
[ ] + { } // -> "[object Object]"
+ { } // -> NaN
[]
benar, tetapi tidak true
Array adalah nilai kebenaran, namun tidak sama dengan true
.
! ! [ ] // -> true
[ ] == true // -> false
Berikut tautan ke bagian terkait dalam spesifikasi ECMA-262:
!
)null
itu salah, tapi tidak false
Terlepas dari kenyataan bahwa null
adalah nilai yang salah, itu tidak sama dengan false
.
! ! null ; // -> false
null == false ; // -> false
Pada saat yang sama, nilai falsy lainnya, seperti 0
atau ''
sama dengan false
.
0 == false ; // -> true
"" == false ; // -> true
Penjelasannya sama seperti contoh sebelumnya. Ini tautan yang sesuai:
document.all
adalah sebuah objek, tetapi tidak terdefinisi
️ Ini adalah bagian dari API Browser dan tidak akan berfungsi di lingkungan Node.js️
Terlepas dari kenyataan bahwa document.all
adalah objek mirip array dan memberikan akses ke node DOM di halaman, ia merespons fungsi typeof
sebagai undefined
.
document . all instanceof Object ; // -> true
typeof document . all ; // -> 'undefined'
Pada saat yang sama, document.all
tidak sama dengan undefined
.
document . all === undefined ; // -> false
document . all === null ; // -> false
Namun pada saat yang sama:
document . all == null ; // -> true
document.all
dulunya merupakan cara untuk mengakses elemen DOM, khususnya dengan IE versi lama. Meskipun belum pernah menjadi standar, ini digunakan secara luas di kode JS lama. Ketika standar berkembang dengan API baru (sepertidocument.getElementById
) panggilan API ini menjadi tidak berlaku lagi dan komite standar harus memutuskan apa yang harus dilakukan dengannya. Karena penggunaannya yang luas, mereka memutuskan untuk mempertahankan API tersebut tetapi melakukan pelanggaran yang disengaja terhadap spesifikasi JavaScript. Alasan mengapa ia meresponsfalse
ketika menggunakan Perbandingan Kesetaraan Ketat denganundefined
sementaratrue
ketika menggunakan Perbandingan Kesetaraan Abstrak adalah karena pelanggaran yang disengaja terhadap spesifikasi yang secara eksplisit mengizinkan hal itu.— “Fitur usang - document.all” di WhatWG - Spesifikasi HTML — “Bab 4 - ToBoolean - Nilai palsu” di YDKJS - Jenis & Tata Bahasa
Number.MIN_VALUE
adalah angka terkecil yang lebih besar dari nol:
Number . MIN_VALUE > 0 ; // -> true
Number.MIN_VALUE
adalah5e-324
, yaitu bilangan positif terkecil yang dapat direpresentasikan dalam presisi float, yaitu sedekat mungkin dengan nol. Ini mendefinisikan resolusi terbaik yang dapat diberikan oleh float kepada Anda.Sekarang nilai terkecil secara keseluruhan adalah
Number.NEGATIVE_INFINITY
meskipun sebenarnya bukan numerik.— “Mengapa
0
kurang dariNumber.MIN_VALUE
di JavaScript?” di StackOverflow
️ Bug muncul di V8 v5.5 atau lebih rendah (Node.js <=7)️
Anda semua tahu tentang undefinisi is not a function yang mengganggu, tapi bagaimana dengan ini?
// Declare a class which extends null
class Foo extends null { }
// -> [Function: Foo]
new Foo ( ) instanceof null ;
// > TypeError: function is not a function
// > at … … …
Ini bukan bagian dari spesifikasi. Itu hanya bug yang sekarang sudah diperbaiki, jadi kedepannya tidak akan ada masalah lagi.
Ini kelanjutan cerita dengan bug sebelumnya di lingkungan modern (diuji dengan Chrome 71 dan Node.js v11.8.0).
class Foo extends null { }
new Foo ( ) instanceof null ;
// > TypeError: Super constructor null of Foo is not a constructor
Ini bukan bug karena:
Object . getPrototypeOf ( Foo . prototype ) ; // -> null
Jika kelas tidak memiliki konstruktor, panggilan dari rantai prototipe. Namun di induknya tidak memiliki konstruktor. Untuk berjaga-jaga, saya akan mengklarifikasi bahwa null
adalah sebuah objek:
typeof null === "object" ;
Oleh karena itu, Anda dapat mewarisinya (walaupun di dunia OOP untuk istilah seperti itu akan mengalahkan saya). Jadi, Anda tidak dapat memanggil konstruktor nol. Jika Anda mengubah kode ini:
class Foo extends null {
constructor ( ) {
console . log ( "something" ) ;
}
}
Anda melihat kesalahannya:
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
Dan jika Anda menambahkan super
:
class Foo extends null {
constructor ( ) {
console . log ( 111 ) ;
super ( ) ;
}
}
JS membuat kesalahan:
TypeError: Super constructor null of Foo is not a constructor
Bagaimana jika Anda mencoba menambahkan dua array?
[ 1 , 2 , 3 ] + [ 4 , 5 , 6 ] ; // -> '1,2,34,5,6'
Penggabungan terjadi. Langkah demi langkah tampilannya seperti ini:
[ 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" ) ;
Anda telah membuat array dengan 4 elemen kosong. Bagaimanapun juga, Anda akan mendapatkan array dengan tiga elemen, karena tanda koma di belakangnya:
let a = [ , , , ] ;
a . length ; // -> 3
a . toString ( ) ; // -> ',,'
Tanda koma (terkadang disebut "koma terakhir") dapat berguna saat menambahkan elemen, parameter, atau properti baru ke kode JavaScript. Jika Anda ingin menambahkan properti baru, Anda cukup menambahkan baris baru tanpa mengubah baris terakhir sebelumnya jika baris tersebut sudah menggunakan tanda koma. Hal ini membuat perbedaan kontrol versi lebih bersih dan pengeditan kode menjadi lebih mudah.
— Tanda koma di MDN
Kesetaraan array adalah monster di JS, seperti yang Anda lihat di bawah:
[ ] == '' // -> 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
Anda harus memperhatikan contoh di atas dengan cermat! Perilaku tersebut dijelaskan di bagian 7.2.15 Abstrak Perbandingan Kesetaraan spesifikasi.
undefined
dan Number
Jika kita tidak meneruskan argumen apa pun ke konstruktor Number
, kita akan mendapatkan 0
. Nilai undefined
ditugaskan ke argumen formal ketika tidak ada argumen sebenarnya, sehingga Anda mungkin berharap bahwa Number
tanpa argumen mengambil undefined
sebagai nilai parameternya. Namun, saat kita melewati undefined
, kita akan mendapatkan NaN
.
Number ( ) ; // -> 0
Number ( undefined ) ; // -> NaN
Menurut spesifikasinya:
n
menjadi +0
.n
? ToNumber(value)
.undefined
, ToNumber(undefined)
harus mengembalikan NaN
.Inilah bagian yang sesuai:
argument
) parseInt
adalah orang jahat parseInt
terkenal dengan keunikannya:
parseInt ( "f*ck" ) ; // -> NaN
parseInt ( "f*ck" , 16 ) ; // -> 15
Penjelasan: Hal ini terjadi karena parseInt
akan terus mengurai karakter demi karakter hingga mengenai karakter yang tidak diketahuinya. f
dalam 'f*ck'
adalah angka heksadesimal 15
.
Mengurai Infinity
menjadi integer adalah sesuatu…
//
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
Hati-hati dengan penguraian null
juga:
parseInt ( null , 24 ) ; // -> 23
Penjelasan:
Itu mengubah
null
menjadi string"null"
dan mencoba mengonversinya. Untuk radix 0 hingga 23, tidak ada angka yang dapat dikonversi, sehingga ia mengembalikan NaN. Pada angka 24,"n"
, huruf ke-14, ditambahkan ke sistem angka. Pada 31,"u"
, huruf ke-21, ditambahkan dan seluruh string dapat didekodekan. Pada 37 tidak ada lagi kumpulan angka valid yang dapat dihasilkan danNaN
dikembalikan.— “parseInt(null, 24) === 23… tunggu, apa?” di StackOverflow
Jangan lupa tentang oktal:
parseInt ( "06"