Ini valid dan mengembalikan string "10"
dalam JavaScript ( lebih banyak contoh di sini ):
console.log(++[[]][+[]]+[+[]])
Mengapa? Apa yang terjadi disini?
javascript
syntax
SapuSeven
sumber
sumber
+[]
melemparkan array kosong ke0
... lalu buang siang ...;)Jawaban:
Jika kita membaginya, kekacauannya sama dengan:
Dalam JavaScript, memang benar begitu
+[] === 0
.+
mengubah sesuatu menjadi angka, dan dalam hal ini akan turun ke+""
atau0
(lihat detail spesifikasi di bawah).Karena itu, kita dapat menyederhanakannya (
++
didahulukan+
):Karena
[[]][0]
berarti: dapatkan elemen pertama dari[[]]
, memang benar bahwa:[[]][0]
mengembalikan array batin ([]
). Karena referensi salah untuk mengatakan[[]][0] === []
, tetapi mari kita panggil array dalamA
untuk menghindari notasi yang salah.++
sebelum operannya berarti "bertambah satu demi satu dan kembalikan hasil yang bertambah". Jadi++[[]][0]
setara denganNumber(A) + 1
(atau+A + 1
).Sekali lagi, kita dapat menyederhanakan kekacauan menjadi sesuatu yang lebih mudah dibaca. Mari kita ganti
[]
kembali untukA
:Sebelum
+[]
dapat memaksa array ke dalam angka0
, ia harus dipaksa menjadi string terlebih dahulu, yaitu""
, sekali lagi. Akhirnya,1
ditambahkan, yang menghasilkan1
.(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 1) === 1
Mari kita semakin menyederhanakannya:
Juga, ini benar dalam JavaScript:,
[0] == "0"
karena menggabungkan array dengan satu elemen. Bergabung akan menggabungkan elemen-elemen yang dipisahkan oleh,
. Dengan satu elemen, Anda dapat menyimpulkan bahwa logika ini akan menghasilkan elemen pertama itu sendiri.Dalam hal ini,
+
lihat dua operan: angka dan array. Sekarang mencoba untuk memaksa keduanya menjadi tipe yang sama. Pertama, array dipaksa ke dalam string"0"
, selanjutnya, nomor dipaksa ke dalam string ("1"
). Jumlah+
String===
String .Detail spesifikasi untuk
+[]
:Ini cukup membingungkan, tetapi untuk dilakukan
+[]
, pertama itu sedang dikonversi ke string karena itulah yang+
mengatakan:ToNumber()
mengatakan:ToPrimitive()
mengatakan:[[DefaultValue]]
mengatakan:The
.toString
array mengatakan:Jadi
+[]
turun ke+""
, karena[].join() === ""
.Sekali lagi,
+
didefinisikan sebagai:ToNumber
didefinisikan""
sebagai:Jadi
+"" === 0
, dan karenanya+[] === 0
.sumber
true
jika nilai dan jenisnya sama.0 == ""
kembalitrue
(sama setelah konversi tipe), tapi0 === ""
inifalse
(tidak jenis yang sama).1 + [0]
, bukan"1" + [0]
, karena++
operator awalan ( ) selalu mengembalikan nomor. Lihat bclary.com/2004/11/07/#a-11.4.4++[[]][0]
mengembalikan memang1
, tetapi++[]
melempar kesalahan. Ini luar biasa karena sepertinya++[[]][0]
tidak mendidih++[]
. Apakah Anda mungkin tahu mengapa++[]
melempar kesalahan sedangkan++[[]][0]
tidak?PutValue
panggilan (dalam terminologi ES3, 8.7.2) dalam operasi awalan.PutValue
membutuhkan Referensi sedangkan[]
sebagai ekspresi sendiri tidak menghasilkan Referensi. Ekspresi yang berisi referensi variabel (katakanlah kita sebelumnya didefinisikanvar a = []
kemudian++a
berfungsi) atau akses properti objek (seperti[[]][0]
) menghasilkan Referensi. Dalam istilah yang lebih sederhana, operator awalan tidak hanya menghasilkan nilai, tetapi juga membutuhkan tempat untuk meletakkan nilai itu.var a = []; ++a
,a
adalah 1. Setelah mengeksekusi++[[]][0]
, array yang dibuat oleh[[]]
ekspresi sekarang hanya berisi angka 1 pada indeks 0.++
membutuhkan Referensi untuk melakukan ini.Kemudian kami memiliki rangkaian string
sumber
===
daripada=>
?Berikut ini diadaptasi dari posting blog yang menjawab pertanyaan ini yang saya posting ketika pertanyaan ini masih ditutup. Tautan adalah ke (salinan HTML dari) spesifikasi ECMAScript 3, masih merupakan dasar untuk JavaScript di peramban web yang umum digunakan saat ini.
Pertama, sebuah komentar: ungkapan semacam ini tidak akan pernah muncul di lingkungan produksi (waras) apa pun dan hanya digunakan sebagai latihan dalam seberapa baik pembaca mengetahui tepi kotor JavaScript. Prinsip umum bahwa operator JavaScript yang secara implisit mengkonversi antar jenis berguna, seperti juga beberapa konversi umum, tetapi banyak detail dalam hal ini tidak.
Ekspresi
++[[]][+[]]+[+[]]
awalnya mungkin terlihat agak mengesankan dan tidak jelas, tetapi sebenarnya relatif mudah dipecah menjadi ekspresi yang terpisah. Di bawah ini saya hanya menambahkan tanda kurung untuk kejelasan; Saya dapat meyakinkan Anda bahwa mereka tidak mengubah apa pun, tetapi jika Anda ingin memverifikasi itu jangan ragu untuk membaca tentang operator pengelompokan . Jadi, ungkapan itu bisa ditulis dengan lebih jelasDengan memecah ini, kita dapat menyederhanakan dengan mengamati yang
+[]
mengevaluasi0
. Untuk memuaskan diri Anda sendiri mengapa ini benar, periksa operator + unary dan ikuti jejak sedikit berliku yang berakhir dengan ToPrimitive mengubah array kosong menjadi string kosong, yang akhirnya dikonversi0
oleh ToNumber . Kami sekarang dapat mengganti0
untuk setiap instance dari+[]
:Sudah lebih sederhana. Adapun
++[[]][0]
, itu adalah kombinasi dari operator kenaikan awalan (++
), array literal mendefinisikan array dengan elemen tunggal yang merupakan array kosong ([[]]
) dan accessor properti ([0]
) dipanggil pada array yang ditentukan oleh array literal.Jadi, kita bisa menyederhanakan
[[]][0]
menjadi adil[]
dan sudah++[]
, kan? Sebenarnya, ini bukan masalahnya karena mengevaluasi++[]
melempar kesalahan, yang awalnya mungkin membingungkan. Namun, sedikit pemikiran tentang sifat++
memperjelas: ini digunakan untuk meningkatkan variabel (misalnya++i
) atau properti objek (misalnya++obj.count
). Tidak hanya mengevaluasi ke suatu nilai, itu juga menyimpan nilai itu di suatu tempat. Dalam kasus++[]
, tidak ada tempat untuk meletakkan nilai baru (apa pun itu) karena tidak ada referensi ke properti objek atau variabel untuk diperbarui. Dalam istilah tertentu, ini dicakup oleh operasi PutValue internal , yang disebut oleh operator kenaikan awalan.Jadi, apa fungsinya
++[[]][0]
? Nah, dengan logika yang sama seperti+[]
, array batin dikonversi ke0
dan nilai ini bertambah dengan1
memberi kami nilai akhir1
. Nilai properti0
di array luar diperbarui ke1
dan seluruh ekspresi dievaluasi1
.Ini meninggalkan kita
... yang merupakan penggunaan sederhana dari operator penjumlahan . Kedua operan pertama dikonversi menjadi primitif dan jika salah satu nilai primitif adalah string, penggabungan string dilakukan, jika tidak penambahan numerik dilakukan.
[0]
dikonversi ke"0"
, jadi string concatenation digunakan, menghasilkan"10"
.Sebagai penutup akhir, sesuatu yang mungkin tidak segera terlihat adalah bahwa mengesampingkan salah satu
toString()
atauvalueOf()
metodeArray.prototype
akan mengubah hasil ekspresi, karena keduanya diperiksa dan digunakan jika ada saat mengubah suatu objek menjadi nilai primitif. Misalnya berikut ini... menghasilkan
"NaNfoo"
. Mengapa ini terjadi dibiarkan sebagai latihan bagi pembaca ...sumber
Mari kita membuatnya sederhana:
sumber
Yang ini dievaluasi sama tapi sedikit lebih kecil
jadi dievaluasi untuk
Jadi sekarang Anda mengerti, coba yang ini:
sumber
"10"
+ [] mengevaluasi ke 0 [...] lalu menjumlahkan (+ operasi) dengan apa pun yang mengubah konten array menjadi representasi string yang terdiri dari elemen yang digabungkan dengan koma.
Apa pun selain mengambil indeks array (memiliki prioritas lebih dari + operasi) adalah ordinal dan tidak ada yang menarik.
sumber
Mungkin cara terpendek yang mungkin untuk mengevaluasi ekspresi menjadi "10" tanpa angka adalah:
+!+[] + [+[]]
// "10"-~[] + [+[]]
// "10"// ========== Penjelasan ========== \\
+!+[]
:+[]
Konversi ke 0.!0
dikonversi ketrue
.+true
dikonversi ke 1.-~[]
=-(-1)
yang adalah 1[+[]]
:+[]
Konversi ke 0.[0]
adalah larik dengan elemen tunggal 0.Kemudian JS mengevaluasi
1 + [0]
, dengan demikianNumber + Array
ekspresi. Kemudian spesifikasi ECMA berfungsi:+
operator mengubah kedua operan menjadi string dengan memanggiltoString()/valueOf()
fungsi dariObject
prototipe dasar . Ini beroperasi sebagai fungsi aditif jika kedua operan dari ekspresi hanya angka. Kuncinya adalah bahwa array dengan mudah mengubah elemen-elemen mereka menjadi representasi string gabungan.Beberapa contoh:
Ada pengecualian yang baik bahwa dua
Objects
hasil tambahan dalamNaN
:sumber
+ '' atau + [] mengevaluasi 0.
sumber
[]
adalah tidak setara dengan""
. Pertama elemen diekstraksi, lalu dikonversi oleh++
.Langkah demi langkah itu,
+
ubah nilainya menjadi angka dan jika Anda menambahkan ke array kosong+[]
... karena kosong dan sama dengan0
, itu akanJadi dari sana, sekarang lihat kode Anda, itu
++[[]][+[]]+[+[]]
...Dan ada plus di antara mereka
++[[]][+[]]
+[+[]]
Jadi ini
[+[]]
akan kembali[0]
karena mereka memiliki array kosong yang akan dikonversi ke0
dalam array lain ...Jadi seperti bayangkan, nilai pertama adalah array 2 dimensi dengan satu array di dalam ... sehingga
[[]][+[]]
akan sama dengan[[]][0]
yang akan kembali[]
...Dan pada akhirnya
++
mengubahnya dan meningkatkannya menjadi1
...Jadi bisa dibayangkan,
1
+"0"
akan"10"
...sumber