Pertanyaan Mengapa ++ [[]] [+ []] + [+ []] mengembalikan string "10"?


Ini valid dan mengembalikan string "10" di JavaScript (lebih banyak contoh di sini):

++[[]][+[]]+[+[]]

Mengapa? Apa yang terjadi disini?


1439
2017-08-26 08:56


asal


Jawaban:


Jika kita membaginya, kekacauan itu sama dengan:

++[[]][+[]]
+
[+[]]

Di JavaScript, memang benar itu +[] === 0. + mengubah sesuatu menjadi angka, dan dalam hal ini akan turun ke +"" atau 0 (lihat detail spesifikasi di bawah).

Oleh karena itu, kita dapat menyederhanakannya (++ memiliki presenden +):

++[[]][0]
+
[0]

Karena [[]][0] berarti: dapatkan elemen pertama dari [[]], memang benar bahwa:

  • [[]][0] mengembalikan array dalam ([]). Karena referensi itu salah untuk dikatakan [[]][0] === [], tapi mari kita sebut array batin A untuk menghindari notasi yang salah.
  • ++[[]][0] == A + 1, sejak ++ berarti 'pertambahan satu'.
  • ++[[]][0] === +(A + 1); dengan kata lain, itu akan selalu menjadi angka (+1 tidak selalu mengembalikan angka, sedangkan ++ selalu - terima kasih kepada Tim Down untuk menunjukkan ini).

Sekali lagi, kita dapat menyederhanakan kekacauan menjadi sesuatu yang lebih mudah dibaca. Mari gantikan [] kembali untuk A:

+([] + 1)
+
[0]

Dalam JavaScript, ini juga benar: [] + 1 === "1", karena [] == "" (Bergabung dengan array kosong), jadi:

  • +([] + 1) === +("" + 1), dan
  • +("" + 1) === +("1"), dan
  • +("1") === 1

Mari kita menyederhanakannya lebih jauh:

1
+
[0]

Juga, ini benar dalam JavaScript: [0] == "0", karena itu bergabung dengan array dengan satu elemen. Bergabung akan menggabungkan elemen yang dipisahkan oleh ,. Dengan satu elemen, Anda dapat menyimpulkan bahwa logika ini akan menghasilkan elemen pertama itu sendiri.

Jadi, pada akhirnya kita mendapatkan (angka + string = string):

1
+
"0"

=== "10" // Yay!

Detail spesifikasi untuk +[]:

Ini cukup labirin, tetapi harus dilakukan +[], pertama kali diubah menjadi string karena itulah + mengatakan:

11.4.6 Unary + Operator

Operator unary + mengubah operand menjadi tipe Number.

Produksi UnaryExpression: + UnaryExpression dievaluasi sebagai berikut:

  1. Biarkan expr menjadi hasil dari mengevaluasi UnaryExpression.

  2. Return ToNumber (GetValue (expr)).

ToNumber() mengatakan:

Obyek

Terapkan langkah-langkah berikut:

  1. Biarkan primValue menjadi ToPrimitive (argumen input, String petunjuk).

  2. Return ToString (primValue).

ToPrimitive() mengatakan:

Obyek

Kembalikan nilai default untuk Object. Nilai default suatu objek diambil dengan memanggil metode internal [[DefaultValue]] objek, meneruskan pilihan PreferredType. Perilaku metode internal [[DefaultValue]] ditentukan oleh spesifikasi ini untuk semua objek ECMAScript asli di 8.12.8.

[[DefaultValue]] mengatakan:

8.12.8 [[NilaiVideo]] (petunjuk)

Ketika [[DefaultValue]] metode internal O dipanggil dengan String petunjuk, langkah-langkah berikut ini diambil:

  1. Biarkan toString menjadi hasil dari pemanggilan metode internal [[Dapatkan]] objek O dengan argumen "toString".

  2. Jika IsCallable (toString) benar maka,

Sebuah. Misalkan hasil dari pemanggilan metode internal [[Call]] dari toString, dengan O sebagai nilai ini dan daftar argumen kosong.

b. Jika str adalah nilai primitif, kembalikan str.

Itu .toString dari array mengatakan:

15.4.4.2 Array.prototype.toString ()

Ketika metode toString dipanggil, langkah-langkah berikut ini diambil:

  1. Biarkan array menjadi hasil pemanggilan ToObject pada nilai ini.

  2. Biarlah hasil dari pemanggilan metode internal [[Get]] dari array dengan argumen "join".

  3. Jika IsCallable (func) salah, maka biarkan func menjadi metode standar built-in Object.prototype.toString (15.2.4.2).

  4. Kembalikan hasil pemanggilan metode internal [[Call]] untuk menyediakan array sebagai nilai ini dan daftar argumen kosong.

Begitu +[] datang ke +"", karena [].join() === "".

Sekali lagi, itu + didefinisikan sebagai:

11.4.6 Unary + Operator

Operator unary + mengubah operand menjadi tipe Number.

Produksi UnaryExpression: + UnaryExpression dievaluasi sebagai berikut:

  1. Biarkan expr menjadi hasil dari mengevaluasi UnaryExpression.

  2. Return ToNumber (GetValue (expr)).

ToNumber didefinisikan untuk "" sebagai:

MV StringNumericLiteral ::: [kosong] adalah 0.

Begitu +"" === 0, dan dengan demikian +[] === 0.


1874
2017-08-26 08:58



++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

Lalu kita memiliki penggabungan string

1+[0].toString() = 10

99
2017-09-14 13:54



Berikut ini diadaptasi dari posting blog menjawab pertanyaan ini yang saya posting sementara pertanyaan ini masih tertutup. Tautan ke (salinan HTML) spesifikasi ECMAScript 3, masih merupakan dasar untuk JavaScript di peramban web yang umum digunakan saat ini.

Pertama, komentar: ekspresi semacam ini tidak akan pernah muncul di lingkungan produksi (waras) dan hanya berguna sebagai latihan dalam seberapa baik pembaca mengetahui sisi kotor JavaScript. Prinsip umum bahwa operator JavaScript secara implisit mengkonversi antar jenis adalah berguna, seperti juga beberapa konversi umum, tetapi banyak detail dalam hal ini tidak.

Ekspresi ++[[]][+[]]+[+[]] mungkin awalnya terlihat agak mengesankan dan tidak jelas, tetapi sebenarnya relatif mudah terurai 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, maka jangan ragu untuk membaca tentang operator pengelompokan. Jadi, ungkapan itu dapat ditulis dengan lebih jelas sebagai

( ++[[]][+[]] ) + ( [+[]] )

Mendobrak ini, kita dapat menyederhanakannya dengan mengamati itu +[] mengevaluasi ke 0. Untuk memuaskan diri sendiri mengapa ini benar, periksa unary + operator dan ikuti jejak yang sedikit berliku-liku yang berakhir dengan ToPrimitive mengubah array kosong menjadi string kosong, yang akhirnya diubah menjadi 0 oleh ToNumber. Kami sekarang bisa mengganti 0 untuk setiap contoh +[]:

( ++[[]][0] ) + [0]

Sudah lebih sederhana. Seperti untuk ++[[]][0], itu kombinasi dari operator penambahan awalan (++), sebuah array literal mendefinisikan sebuah array dengan elemen tunggal yang merupakan array kosong ([[]]) dan a pengakses properti ([0]) memanggil array yang ditentukan oleh array literal.

Jadi, kita bisa menyederhanakan [[]][0] untuk adil [] dan kita mempunyai ++[]benar? Sebenarnya, ini tidak terjadi karena mengevaluasi ++[] melempar kesalahan, yang awalnya tampak membingungkan. Namun, sedikit memikirkan sifat alami ++ membuatnya menjadi jelas: ini digunakan untuk menaikkan variabel (mis. ++i) atau properti objek (mis. ++obj.count). Tidak hanya mengevaluasi ke nilai, itu juga menyimpan nilai itu di suatu tempat. Dalam kasus ++[], tidak memiliki tempat untuk menempatkan nilai baru (apa pun itu) karena tidak ada referensi ke properti objek atau variabel untuk memperbarui. Dalam hal spesifik, ini ditutupi oleh internal PutValue operasi, yang disebut oleh operator penambahan awalan.

Jadi, apa yang terjadi ++[[]][0] melakukan? Nah, dengan logika yang sama seperti +[], array bagian dalam dikonversi menjadi 0 dan nilai ini bertambah dengan 1 untuk memberi kita nilai akhir 1. Nilai properti 0 di luar array diperbarui ke 1 dan seluruh ekspresi mengevaluasi 1.

Ini membuat kita bersama

1 + [0]

... yang merupakan penggunaan sederhana dari operator tambahan. Kedua operand adalah yang pertama dikonversi menjadi primitif dan jika salah satu nilai primitif adalah string, penggabungan string dilakukan, selain itu penambahan numerik dilakukan. [0] mengonversi menjadi "0", jadi penggabungan string digunakan, menghasilkan "10".

Sebagai penutup terakhir, sesuatu yang mungkin tidak segera terlihat adalah bahwa mengesampingkan salah satunya toString() atau valueOf() metode Array.prototype akan mengubah hasil ekspresi, karena keduanya diperiksa dan digunakan jika ada saat mengubah suatu objek menjadi nilai primitif. Misalnya, berikut ini

Array.prototype.toString = function() {
  return "foo";
};
++[[]][+[]]+[+[]]

... menghasilkan "NaNfoo". Mengapa ini terjadi dibiarkan sebagai latihan bagi pembaca ...


57
2017-12-30 15:41



Mari kita sederhananya:

++[[]][+[]]+[+[]] = "10"

var a = [[]][+[]];
var b = [+[]];

// so a == [] and b == [0]

++a;

// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:

1 + "0" = "10"

21
2017-12-28 23:13



Yang satu ini bernilai sama tetapi sedikit lebih kecil

+!![]+''+(+[])
  • [] - adalah larik yang dikonversi yang dikonversi ke 0 saat Anda menambah atau mengurangi darinya, jadi karenanya + [] = 0
  • ! [] - mengevaluasi ke false, jadi karenanya !! [] mengevaluasi ke true
  • + !! [] - mengubah nilai true ke nilai numerik yang bernilai true, jadi dalam kasus ini 1
  • + '' - menambahkan string kosong ke ekspresi yang menyebabkan angka dikonversi menjadi string
  • + [] - mengevaluasi hingga 0

jadi dievaluasi untuk

+(true) + '' + (0)
1 + '' + 0
"10"

Jadi sekarang Anda punya itu, coba yang ini:

_=$=+[],++_+''+$

13
2017-08-26 08:58



+ [] mengevaluasi hingga 0 [...] kemudian menjumlahkan (operasi +) dengan apa pun yang mengubah konten array ke representasi string yang terdiri dari unsur-unsur yang tergabung dengan koma.

Sesuatu yang lain seperti mengambil indeks array (memiliki prioritas lebih besar dari operasi +) adalah ordinal dan tidak ada yang menarik.


7
2017-12-30 08:10



Mungkin cara yang sesingkat mungkin untuk mengevaluasi ekspresi ke "10" tanpa digit adalah:

+!+[] + [+[]] // "10"

-~[] + [+[]]  // "10"

// ========== Penjelasan ========== \\

+!+[] : +[] Mengonversi ke 0. !0 mengonversi menjadi true. +true mengkonversi ke 1. -~[] = -(-1) yaitu 1

[+[]] : +[] Mengonversi ke 0. [0] adalah larik dengan elemen tunggal 0.

Kemudian JS mengevaluasi 1 + [0], jadi Number + Array ekspresi. Kemudian spesifikasi ECMA berfungsi: + operator mengubah kedua operand ke string dengan memanggil toString()/valueOf() fungsi dari basis Object prototipe. Ini beroperasi sebagai fungsi tambahan jika kedua operand dari ekspresi hanya angka saja. Triknya adalah bahwa array dengan mudah mengubah elemen mereka menjadi representasi string yang digabungkan.

Beberapa contoh:

1 + {} //    "1[object Object]"
1 + [] //    "1"
1 + new Date() //    "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"

Ada pengecualian yang bagus Objects hasil tambahan dalam NaN:

[] + []   //    ""
[1] + [2] //    "12"
{} + {}   //    NaN
{a:1} + {b:2}     //    NaN
[1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"

4