Mengapa! {} [True] mengevaluasi kebenaran di JavaScript?

131

{}[true]adalah [true]dan ![true]seharusnya false.

Jadi mengapa !{}[true]harus dievaluasi true?

pengguna2430508
sumber
30
var o = {}; o[true] === undefined.
azz
2
Penjelasan di sini kemungkinan akan sangat mirip dengan keanehan yang dibahas pada pertanyaan sebelumnya
IMSoP
45
"Karena Javascript itu konyol" mungkin bukan jawaban yang Anda cari.
georg
2
Seperti yang disebutkan, jika Anda mendapatkan {}[true] === [true]dari konsol, itu karena itu memperlakukan {}sebagai blok kode kosong, bukan objek.
azz
3
jika itu dapat membantu, cobalah untuk membandingkan {}dan ({})di konsol Anda (atau {}[true]dan ({})[true]). Juga, karena tidak ada yang menyebutkannya, objek [benar] dievaluasi ke objek ["benar"].
BiAiB

Jawaban:

172

Saya percaya itu karena plain {}[true]diuraikan sebagai blok pernyataan kosong (bukan objek literal) diikuti oleh array yang berisi true, yaitu true.

Di sisi lain, menerapkan !operator membuat parser mengartikan {}sebagai objek literal, sehingga berikut ini {}[true]menjadi akses anggota yang kembali undefined, dan !{}[true]memang true(sebagaimana !undefinedadanya true).

Frédéric Hamidi
sumber
25
Fakta bahwa! Tidak terdefinisi benar, di sisi lain, masih dapat dimaafkan.
evilcandybag
87
@ evilcandybag: Sama sekali tidak. undefinedadalah falsy (sesuatu yang sering kita andalkan - if (obj.maybeExists) ...), sehingga masuk akal logis yang !undefinedbenar.
josh3736
8
@ Astaga, saya pikir evilcandybag lebih suka perilaku yang mirip nulldalam beberapa bahasa, dengan !undefinedmenjadi sama dengan undefined. Tapi itu tidak terjadi di Javascript.
Frédéric Hamidi
6
@evilcandybag: logis untuk mengatakan bahwa sesuatu yang not undefined( !undefined) harus didefinisikan. Jika sesuatu didefinisikan maka biasanya ditafsirkan sebagai true.
OozeMeister
7
@Cruncher Jika a tidak terdefinisi, dan b tidak terdefinisi, bagaimana mungkin kita tahu bahwa a! = B? Khususnya ketika karakteristik hanya diketahui dari dua variabel persis sama.
LJ2
44

Karena {}[true]tidak kembali true, tetapi undefined, dan undefineddievaluasi sebagai false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true
dooxe
sumber
21
Jika Anda mengevaluasi {}[true]di konsol, Anda mendapatkan [true], karena {}ditafsirkan sebagai blok kode kosong, bukan objek. Ini semua tentang konteks dan ambiguitas {}.
IMSoP
1
@IMSoP tetapi mengapa {key:"value"}[1,2,3];juga mengevaluasi [1,2,3]?
t.niese
3
@ t.niese, karena diuraikan sebagai blok pernyataan yang berisi label ( key:) dan string literal ( "value"), diikuti oleh array. Parser masih tidak melihat objek literal.
Frédéric Hamidi
1
@ FrédéricHamidi ah ya, itu saja. Saya menekan label ^^
t.niese
1
@dooxe Baca jawaban lain; itu semua tentang konteks di mana ia ditafsirkan. Jika Anda membungkusnya alert()atau console.log(), atau menetapkannya ke variabel, Anda mengubah konteksnya, itulah sebabnya ia tidak berperilaku sama seperti mengetiknya sendiri di konsol.
IMSoP
27

Karena

{}[true]

mengevaluasi untuk undefined, dan !undefinedadalah true.

Dari @schlingel:

truedigunakan sebagai kunci dan {}sebagai peta hash. Tidak ada properti dengan kunci truesehingga kembali undefined. Tidak undefinedini true, seperti yang diharapkan.

Sesi konsol ( Node.js [0.10.17] ):

> {}[true]
undefined
> !{}[true]
true
> [true]
[ true ]
> ![true]
false
>

Namun, di konsol Google Chrome :

> !{}[true]
true

Jadi, tidak ada inkonsistensi. Anda mungkin menggunakan versi lama JavaScript VM. Bagi mereka yang membutuhkan bukti lebih lanjut:

Masukkan deskripsi gambar di sini

MEMPERBARUI

Dengan Firefox , itu juga mengevaluasi untuk true:

Masukkan deskripsi gambar di sini

Game Brainiac
sumber
Tidak jika Anda melakukannya eval('{}[true]')atau mengetiknya di konsol. Kemudian misalnya als {}"test"adalah testatau bahkan {key:"value"}"test"adalah test.
t.niese
Menarik, di mesin js mana Anda mengujinya?
t.niese
@ t.niese Saya baru saja mengetiknya di konsol node saya, dan inilah yang saya dapatkan.
Game Brainiac
Hanya karena penasaran. Apakah {}[true];(dengan ;) kembali [true]untuk Anda, karena ini dia?
t.niese
2
Alasan downvote guys? Ada jawaban yang hampir identik dengan yang ini dengan 8 suara, dan saya mendapatkan downvote? Apa kesalahan yang telah aku perbuat?
Game Brainiac
23

Alasan kebingungan adalah kesalahpahaman dari pernyataan pertama Anda:

{}[true] adalah [true]

Apa yang Anda lihat ketika Anda menjalankannya adalah hasil dari ambiguitas. Javascript memiliki seperangkat aturan yang ditentukan tentang cara menangani ambiguitas seperti ini, dan dalam hal ini, Javascript memecah apa yang Anda lihat sebagai pernyataan signle menjadi dua pernyataan terpisah.

Jadi Javascript melihat kode di atas sebagai dua pernyataan terpisah: Pertama, ada {}, dan kemudian ada yang sepenuhnya terpisah [true]. Pernyataan kedua adalah apa yang memberi Anda hasilnya [true]. Pernyataan pertama secara {}efektif diabaikan sepenuhnya.

Anda dapat membuktikan ini dengan mencoba yang berikut:

({}[true])

yaitu membungkus semuanya dalam tanda kurung untuk memaksa penerjemah untuk membacanya sebagai satu pernyataan.

Sekarang Anda akan melihat bahwa nilai sebenarnya dari pernyataan Anda adalah undefined. (ini juga akan membantu kita nanti untuk memahami bagian selanjutnya)

Sekarang kita tahu bahwa bagian awal dari pertanyaan Anda adalah ikan haring merah, jadi mari kita lanjutkan ke bagian akhir dari pertanyaan:

Jadi mengapa! {} [Benar] dievaluasi menjadi benar?

Di sini, kita memiliki pernyataan yang sama, tetapi dengan !menambahkan di depannya.

Dalam hal ini, aturan Javascript memberitahu untuk mengevaluasi semuanya sebagai satu pernyataan.

Lihat kembali apa yang terjadi ketika kami membungkus pernyataan sebelumnya dalam tanda kurung; kami punya undefined. Kali ini, kita secara efektif melakukan hal yang sama, tetapi menempatkannya !di depannya. Jadi kode Anda dapat disederhanakan !undefined, yaitu true.

Semoga itu menjelaskan sedikit.

Ini adalah binatang yang kompleks, tetapi pelajaran untuk belajar di sini adalah menggunakan tanda kurung di sekitar pernyataan Anda saat mengevaluasi mereka di konsol, untuk menghindari hasil palsu seperti ini.

Spudley
sumber
2
Saya pikir {}[true]itu tidak benar, hanya ambigu . Ini dapat diartikan sebagai "blok kode kosong diikuti oleh array literal" atau "objek literal tanpa properti, yang properti sedang diakses". Saya tidak tahu apakah yang pertama secara teknis adalah kasus ASI (banyak bahasa tidak akan menempatkan titik koma di sana), tetapi interpretasi yang peka konteks itulah yang menjadi inti masalah.
IMSoP
@IMSoP - Saya sudah mengedit jawaban sebelum Anda memposting komentar. :)
Spudley
1
Ia masih mengatakan "{} [true] sebenarnya tidak valid sama sekali" tepat di awal jawaban.
IMSoP
Juga, OP tidak mengatakan " {}[true]adalah true" kata mereka " {}[true]adalah [true]", yang merupakan salah satu dari dua interpretasi valid dari pernyataan ambigu.
IMSoP
14

{}[true]adalah undefined. Untuk menemukan yang menulis ini:

a = {};
a[true] === undefined // true

atau hanya:

({})[true] === undefined // true

Kita tahu bahwa !undefinedadalah true.


Dari jawaban @Benjamin Gruenbaum :

Alat pengembang Chrome melakukan hal berikut :

  try {
      if (injectCommandLineAPI && inspectedWindow.console) {
          inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
          expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
      }
      var result = evalFunction.call(object, expression);
      if (objectGroup === "console")
          this._lastResult = result;
      return result;
  } 
  finally {
      if (injectCommandLineAPI && inspectedWindow.console)
          delete inspectedWindow.console._commandLineAPI;
  }

Jadi pada dasarnya, ia melakukan callpada objek dengan ekspresi. Ungkapannya adalah:

with ((window && window.console && window.console._commandLineAPI) || {}) {
    {}+{};// <-- This is your code
}

Jadi, seperti yang Anda lihat, ekspresi sedang dievalusi secara langsung, tanpa tanda kurung pembungkus.

Informasi lebih lanjut dapat ditemukan dalam pertanyaan ini .

Ionică Bizău
sumber
10

Jawaban di sini baik, berikut adalah rincian dalam pseudo-code:

  • {}['whatever'] = blok kosong, NewArray ('apa pun') = NewArray ('apa pun')
  • {}[true] = blok kosong, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whthing)) = LogicalNOT (convertToBool (undefined)) = LogicalNOT (false) = true
  • ({}['whatever']) = Pengelompokan (NewObject.whthing) = Pengelompokan (tidak terdefinisi) = tidak terdefinisi
Ben Lesh
sumber
8

Ini terjadi karena {}dalam arti Anda bukan presentasi literal Object, tetapi ruang lingkup kosong (atau blok kode kosong):

{ var a = 1 }[true] // [true] (do the same thing)

Itu hanya mengevaluasi kode di dalam lingkup dan kemudian menunjukkan array Anda.

Dan dari Anda

!{}[true]

Konversi ke int lingkup ini dan mengembalikan array yang sama benar. Tidak ada pemeriksaan bool dalam kode ini.

Dan jika Anda akan mencoba untuk memeriksa hasil dari {}[true]Anda akan mendapatkan false:

{}[true] -> [true] -> ![true] -> false

Karena tidak ada lagi ruang lingkup.

Jadi, !dalam pertanyaan Anda lakukan hal yang sama seperti:

!function() {
   //...
}
antyrat
sumber
Ini lebih mudah dilihat jika Anda melakukannya var x = {}; x[true].
Chris Hayes
1
Saya tidak yakin apa yang Anda maksud dengan "mengkonversi ke dalam lingkup ini"; Saya pikir dengan terkemuka !itu adalah ditafsirkan sebagai objek kosong, tidak ruang lingkup, dan ini adalah perbedaan tersebut.
IMSoP
6
  • {} adalah objek tanpa properti.
  • Karena []segera mengikuti objek, itu berarti "Akses properti dari nama ini" dan bukan "Buat array"
  • trueadalah boolean, tetapi digunakan sebagai nama properti sehingga dilemparkan ke string ( "true")
  • Objek tidak memiliki properti yang disebut true(karena tidak memiliki properti) begitu {}['true']pulaundefined
  • !undefineddilemparkan undefinedke boolean ( false)
  • Operator tidak berubah falsemenjadi true.
Quentin
sumber
2
Dalam kasus {}[true](tanpa konteks lain), {}adalah bukan suatu objek tanpa sifat, itu sebuah blok kode yang kosong.
IMSoP
4

Ayo Main Sedikit Lagi!

Pertama, mari bersenang-senang !:

//----------#01#-----------
{}[true]; //[true]

//----------#02#-----------
var a = {}[true]; 
      console.log(a); //undefined

//----------#03#-----------
{ b: 12345 }[true]; //[true]

//----------#04#-----------
{ b: 12345 }["b"]; //evaluates to ["b"] ?!?

//----------#05#-----------
{ b: 12345 }.b; // "Unexpected token ."

//----------#06#-----------
({ b: 12345 }).b; //12345

//----------#07#-----------
var c = { b: 12345 }.b; 
      console.log(c); //12345

//----------#08#-----------
var c = { b: 12345 }["b"];
      console.log(c); //12345

//----------#09#-----------
{ true: 54321 }[true]; // "SyntaxError: Unexpected token : "

//----------#10#-----------
var d = { true: 54321 }[true]; //No error here ¬¬
      console.log(d); //54321

//----------#11#-----------
!{}[true]; // true

Ok, mari kita coba memahami perilaku gila ini, satu per satu:

1) Di sini, {}diuraikan sebagai blok kode kosong. Tanpa penetapan, negasi, pengelompokan (dengan tanda kurung) atau sintaks apa pun yang menunjukkan kepada parser bahwa ini {}adalah objek literal, asumsi default adalah menganggapnya hanyalah blok kosong yang tidak berguna.

Ini adalah bukti dari perilaku ini:

{ alert(123) }[true]

Kode di atas akan menampilkan peringatan itu secara normal, dan akan dievaluasi seperti [true]halnya dengan cara yang sama {}[true].

Blokir Pernyataan Tanpa Titik Koma

Pernyataan tipe blok tidak perlu titik koma setelahnya.

Misalnya:

for(var i=0; i < 1; i++){}function a(){};alert("Passed here!");if(true){}alert("Passed here too!")

Kedua peringatan ditampilkan.

Jadi, kita dapat melihat bahwa pernyataan blok kosong, tanpa titik koma, valid dan tidak melakukan apa-apa. Dengan cara ini, ketika Anda memasukkan {}[true]di Konsol Alat Pengembang (atau Firebug), nilai yang dievaluasi akan menjadi nilai pernyataan ekspresi terakhir . Dalam hal ini, pernyataan ekspresi terakhir adalah [true].

2) Dalam konteks penugasan, parser akan memastikan bahwa itu {}adalah objek literal. Ketika Anda melakukan var a = {}[true], Anda menghapus ambiguitas dan memberi tip parser yang {}bukan pernyataan blok.
Jadi, di sini, Anda mencoba untuk mendapatkan nilai dengan kunci "true"dari objek kosong. Jelas, tidak ada pasangan nilai kunci dengan nama kunci ini. Dengan cara ini, variabel tidak terdefinisi.

Kata-kata yang dicadangkan sebagai kunci Objek

ECMAScript 5 memungkinkan kunci objek untuk dilindungi undang-undang. Jadi, kunci-kunci berikut ini legal:

var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}

3) Penjelasan yang sama dari contoh 1 . Tapi ... Jika { b: 12345 }bagian itu diperlakukan sebagai pernyataan blok, apa jenis b: 12345pernyataannya ??

... (?????)

Ini pernyataan label , Anda sudah melihatnya sebelumnya ... Ini digunakan dalam loop dan in switch. Berikut adalah beberapa tautan menarik tentang pernyataan label: 1 , (2) [ Cara terbaik untuk melepaskan diri dari loop bersarang di Javascript? , (3) [ Bagaimana cara memecah loop bersarang di javascript? .

CATATAN: Coba saja evaluasi ini:

{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :

Pernyataan label tidak dapat dipisahkan oleh operator koma , Anda harus memisahkannya dengan tanda titik koma. Jadi ini valid:{a: 1; b: 2}

4) Lihat penjelasan untuk contoh 1 dan 3 ...

5) Sekali lagi, kami { b: 12345 }diperlakukan sebagai blok kode, dan Anda mencoba mengakses properti blok kode dengan menggunakan notasi titik , dan jelas, ini tidak diizinkan, dan parser melempar "Unexpected token :"pengecualian.

6) Kode hampir identik dengan contoh di atas, tetapi dengan mengelilingi { b: 12345 }pernyataan dengan operator pengelompokan ekspresi , parser akan tahu bahwa itu adalah objek. Dengan cara ini, Anda dapat mengakses "b"properti secara normal.

7) Ingat contoh 2 , kami punya tugas di sini, parser tahu itu { b: 12345 }adalah objek.

8) Identik dengan contoh di atas, tetapi alih-alih notasi titik, di sini kita menggunakan notasi braket .

9) Saya sudah mengatakan bahwa "identifier: value"sintaks ini di dalam pernyataan blok adalah label. Tapi, Anda juga harus tahu bahwa nama label tidak boleh kata kunci yang dipesan (kebalikan dari nama properti objek). Ketika kami mencoba mendefinisikan label yang disebut "true", kami mendapat a SyntaxError.

10) Sekali lagi, kita berurusan dengan suatu objek. Tidak ada masalah menggunakan kata-kata yang dipesan di sini. =)

11) Akhirnya, kami memiliki ini:!{}[true]

Mari kita pisahkan hal-hal di sini:

a) Dengan melakukan negasi, kami memberi tahu parser bahwa objek {}adalah objek .

b) Seperti yang ditunjukkan pada contoh 2 , {}objek tidak memiliki properti yang dipanggil true, jadi ungkapan ini akan dievaluasi undefined.

c) Hasil akhirnya adalah negasi undefinednilai. Javascript melakukan konversi tipe implikasi , dan undefinednilainya falsy .

d) Jadi, negasi dari falseadalah ... true!

Alcides Queiroz Aguiar
sumber