Array kosong tampaknya sama benar dan salah pada saat yang sama


201

Array kosong benar tetapi mereka juga sama dengan false.

var arr = [];
console.log('Array:', arr);
if (arr) console.log("It's true!");
if (arr == false) console.log("It's false!");
if (arr && arr == false) console.log("...what??");

Saya kira ini karena konversi implisit yang dioperasikan oleh operator kesetaraan.

Adakah yang bisa menjelaskan apa yang terjadi di balik layar?


1
Inilah utas serupa yang harus menjelaskan masalah ini: stackoverflow.com/questions/4226101/…
Rion Williams

2
Catatan, itu arr == truetidak mengevaluasi benar ;-)
Michael Krelin - hacker

5
Wow ... tepat ketika Anda berpikir Anda memiliki semua ini.
harpo

3
Untuk menghindari pemaksaan tipe Javascript WTF, gunakan opeartor kesetaraan yang ketat ===. Kemudian jika Anda ingin menguji kekosongan array, gunakanarr === []
DjebbZ

17
Jika Anda ingin menguji kekosongan array JANGAN gunakan arr === [], karena itu SELALU akan mengembalikan false, karena sisi kanan adalah instantiating array baru, dan variabel di sebelah kiri tidak dapat merujuk ke sesuatu yang baru saja Anda buat. Menguji kekosongan harus dilakukan dengan melihat ke atas arr.length === 0.
Kyle Baker

Jawaban:


274

Anda menguji berbagai hal di sini.

if (arr) dipanggil objek (Array adalah instance dari Objek di JS) akan memeriksa apakah objek tersebut ada, dan mengembalikan true / false.

Ketika Anda menelepon if (arr == false)Anda membandingkan nilai objek ini dan nilai primitif false. Secara internal, arr.toString()disebut, yang mengembalikan string kosong "".

Ini karena toStringdipanggil pada pengembalian Array Array.join(), dan string kosong adalah salah satu nilai salah dalam JavaScript.


2
Bisakah Anda jelaskan mengapa Boolean([])kembali true?
Devy

11
itu dengan konvensi, di JS jika objek dipaksa ke Boolean, mereka selalu dipaksa untuk TRUE. lihat tabel "Boolean context" di: javascript.info/tutorial/object-conversion
Niki

2
@Tanyakan semua objek dalam JavaScript benar, jadi konversi objek apa pun ke Boolean adalah benar. Lihat 2ality.com/2013/08/objects-truthy.html
Thomson

62

Mengenai garis:

if (arr == false) console.log("It's false!");

Mungkin ini akan membantu:

console.log(0 == false) // true
console.log([] == 0) // true
console.log([] == "") // true

Apa yang saya yakini sedang terjadi adalah bahwa boolean falsedipaksa 0untuk dibandingkan dengan objek (sisi kiri). Objek dipaksa ke string (string kosong). Kemudian, string kosong dipaksa menjadi angka, juga, yaitu nol. Dan perbandingan terakhirnya adalah 0== 0, yaitu true.

Sunting: Lihat bagian spesifikasi ini untuk detail tentang cara kerjanya.

Inilah yang terjadi, mulai dari aturan # 1:

1. Jika Tipe (x) berbeda dari Tipe (y), lanjutkan ke langkah 14.

Aturan selanjutnya yang berlaku adalah # 19:

19. Jika Tipe (y) adalah Boolean, kembalikan hasil perbandingan x == ToNumber (y).

Hasilnya ToNumber(false)adalah 0, jadi sekarang kita memiliki:

[] == 0

Sekali lagi, aturan # 1 memberitahu kita untuk melompat ke langkah # 14, tetapi langkah selanjutnya yang benar-benar berlaku adalah # 21:

21. Jika Tipe (x) adalah Objek dan Tipe (y) adalah String atau Angka, kembalikan hasil perbandingan ToPrimitive (x) == y.

Hasilnya ToPrimitive([])adalah string kosong, jadi kita sekarang memiliki:

"" == 0

Sekali lagi, aturan # 1 memberitahu kita untuk melompat ke langkah # 14, tetapi langkah selanjutnya yang benar-benar berlaku adalah # 17:

17. Jika Tipe (x) adalah String dan Tipe (y) adalah Angka, kembalikan hasil perbandingan ToNumber (x) == y.

Hasilnya ToNumber("")adalah 0, yang meninggalkan kita dengan:

0 == 0

Sekarang, kedua nilai memiliki tipe yang sama, jadi langkah-langkahnya berlanjut dari # 1 hingga # 7, yang mengatakan:

7. Jika x adalah nilai angka yang sama dengan y, kembalikan benar.

Jadi kita kembali true.

Secara singkat:

ToNumber(ToPrimitive([])) == ToNumber(false)

2
Referensi bagus! Untuk menghindari kebingungan, mungkin bermanfaat untuk menyebutkan bahwa alasan "aturan berikutnya yang berlaku adalah # 19" meskipun aturan # 1 mengatakan "lanjutkan ke langkah 14", karena langkah 14-18 tidak cocok dengan jenis-jenis aturan. nilai yang dibandingkan.
Sean the Bean

2
Penjelasan yang bagus. Sangat membingungkan bagi saya bahwa array kosong dianggap benar, 0 adalah falsey, namun [] == 0benar. Saya mengerti bagaimana hal ini terjadi berdasarkan penjelasan Anda tentang spec, tetapi sepertinya perilaku bahasa yang aneh dari sudut pandang logis.
bigh_29

7

Untuk melengkapi jawaban Wayne dan mencoba menjelaskan mengapa ToPrimitive([])kembali "", ada baiknya mempertimbangkan dua jenis jawaban yang mungkin untuk pertanyaan 'mengapa'. Jenis jawaban pertama adalah: "karena spesifikasi mengatakan ini adalah bagaimana JavaScript akan berperilaku." Dalam spec ES5, bagian 9.1 , yang menjelaskan hasil ToPrimitive sebagai nilai default untuk Objek:

Nilai default suatu objek diambil dengan memanggil metode internal [[DefaultValue]] objek, melewati petunjuk opsional PreferredType.

Bagian 8.12.8 menjelaskan [[DefaultValue]]metode ini. Metode ini menggunakan "petunjuk" sebagai argumen, dan petunjuk itu bisa berupa String atau Angka. Untuk menyederhanakan masalah dengan mengeluarkan beberapa detail, jika petunjuknya adalah String, maka [[DefaultValue]]mengembalikan nilai toString()jika ada dan mengembalikan nilai primitif dan sebaliknya mengembalikan nilai valueOf(). Jika petunjuknya adalah Angka, prioritas toString()dan valueOf()dibalik sehingga valueOf()disebut pertama dan nilainya dikembalikan jika itu primitif. Jadi, apakah [[DefaultValue]]mengembalikan hasil toString()atau valueOf()tergantung pada PreferredType yang ditentukan untuk objek dan apakah fungsi-fungsi ini mengembalikan nilai primitif.

valueOf()Metode Obyek default hanya mengembalikan objek itu sendiri, yang berarti bahwa kecuali kelas menimpa metode default, valueOf()hanya mengembalikan Objek itu sendiri. Ini adalah kasus untuk Array. [].valueOf()mengembalikan objek []itu sendiri. Karena suatu Arrayobjek bukan primitif, [[DefaultValue]]petunjuknya tidak relevan: nilai kembali untuk array akan menjadi nilai toString().

Mengutip JavaScript David Flanagan : The Definitive Guide , yang merupakan buku luar biasa yang harus menjadi tempat pertama setiap orang untuk mendapatkan jawaban atas pertanyaan-pertanyaan ini:

Rincian konversi objek-ke-angka ini menjelaskan mengapa array kosong dikonversi ke angka 0 dan mengapa array dengan elemen tunggal juga dapat dikonversi ke angka. Array mewarisi metode default valueOf () yang mengembalikan objek daripada nilai primitif, jadi konversi array-ke-angka bergantung pada metode toString (). Array kosong dikonversi ke string kosong. Dan string kosong dikonversi ke angka 0. Array dengan elemen tunggal dikonversi ke string yang sama dengan yang dilakukan oleh satu elemen. Jika array berisi nomor tunggal, angka itu dikonversi ke string, dan kemudian kembali ke angka.

Jenis jawaban kedua untuk pertanyaan "mengapa", selain "karena spesifikasi mengatakan", memberikan beberapa penjelasan mengapa perilaku tersebut masuk akal dari perspektif desain. Pada masalah ini saya hanya bisa berspekulasi. Pertama, bagaimana seseorang mengonversi array menjadi angka? Satu-satunya kemungkinan yang masuk akal yang dapat saya pikirkan adalah untuk mengubah array kosong menjadi 0 dan array non-kosong menjadi 1. Tetapi seperti yang diungkapkan oleh Wayne, array kosong akan dikonversi ke 0 untuk banyak jenis perbandingan. Di luar ini, sulit untuk memikirkan nilai pengembalian primitif yang masuk akal untuk Array.valueOf (). Jadi orang dapat berargumen bahwa lebih masuk akal untuk Array.valueOf()menjadi default dan mengembalikan Array itu sendiri, yang mengarah toString()ke hasil yang digunakan oleh ToPrimitive. Lebih masuk akal untuk mengubah Array menjadi string, daripada angka.

Selain itu, seperti yang ditunjukkan oleh kutipan Flanagan, keputusan desain ini memang memungkinkan jenis perilaku menguntungkan tertentu. Misalnya:

var a = [17], b = 17, c=1;
console.log(a==b);      // <= true
console.log(a==c);      // <= false

Perilaku ini memungkinkan Anda untuk membandingkan array elemen tunggal dengan angka dan mendapatkan hasil yang diharapkan.


Terima kasih atas jawaban ini, penjelasan yang cukup rinci yang kurang dari pertanyaan.
Estus Flask

3
console.log('-- types: undefined, boolean, number, string, object --');
console.log(typeof undefined);  // undefined
console.log(typeof null);       // object
console.log(typeof NaN);        // number
console.log(typeof false);      // boolean
console.log(typeof 0);          // number
console.log(typeof "");         // string
console.log(typeof []);         // object
console.log(typeof {});         // object

console.log('-- Different values: NotExist, Falsy, NaN, [], {} --');
console.log('-- 1. NotExist values: undefined, null have same value --');
console.log(undefined == null); // true

console.log('-- 2. Falsy values: false, 0, "" have same value --');
console.log(false == 0);        // true
console.log(false == "");       // true
console.log(0 == "");           // true

console.log('-- 3. !NotExist, !Falsy, and !NaN return true --');
console.log(!undefined);        // true
console.log(!null);             // true

console.log(!false);            // true
console.log(!"");               // true
console.log(!0);                // true

console.log(!NaN);              // true

console.log('-- 4. [] is not falsy, but [] == false because [].toString() returns "" --');
console.log(false == []);       // true
console.log([].toString());     // ""

console.log(![]);               // false

console.log('-- 5. {} is not falsy, and {} != false, because {}.toString() returns "[object Object]" --');
console.log(false == {});       // false
console.log({}.toString());     // [object Object]

console.log(!{});               // false

console.log('-- Comparing --');
console.log('-- 1. string will be converted to number or NaN when comparing with a number, and "" will be converted to 0 --');
console.log(12 < "2");          // false
console.log("12" < "2");        // true
console.log("" < 2);            // true

console.log('-- 2. NaN can not be compared with any value, even if NaN itself, always return false --');
console.log(NaN == NaN);        // false

console.log(NaN == null);       // false
console.log(NaN == undefined);  // false
console.log(0 <= NaN);          // false
console.log(0 >= NaN);          // false
console.log(undefined <= NaN);  // false
console.log(undefined >= NaN);  // false
console.log(null <= NaN);       // false
console.log(null >= NaN);       // false

console.log(2 <= "2a");         // false, since "2a" is converted to NaN
console.log(2 >= "2a");         // false, since "2a" is converted to NaN

console.log('-- 3. undefined can only == null and == undefined, and can not do any other comparing even if <= undefined --');
console.log(undefined == null);         // true
console.log(undefined == undefined);    // true

console.log(undefined == "");           // false
console.log(undefined == false);        // false
console.log(undefined <= undefined);    // false
console.log(undefined <= null);         // false
console.log(undefined >= null);         // false
console.log(0 <= undefined);            // false
console.log(0 >= undefined);            // false

console.log('-- 4. null will be converted to "" when <, >, <=, >= comparing --');
console.log(12 <= null);        // false
console.log(12 >= null);        // true
console.log("12" <= null);      // false
console.log("12" >= null);      // true

console.log(0 == null);         // false
console.log("" == null);        // false

console.log('-- 5. object, including {}, [], will be call toString() when comparing --');
console.log(12 < {});           // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log(12 > {});           // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log("[a" < {});         // true, since {}.toString() is "[object Object]"
console.log("[a" > {});         // false, since {}.toString() is "[object Object]"
console.log(12 < []);           // false, since {}.toString() is "", and then converted to 0
console.log(12 > []);           // true, since {}.toString() is "", and then converted to 0
console.log("[a" < []);         // false, since {}.toString() is ""
console.log("[a" > []);         // true, since {}.toString() is ""

console.log('-- 6. According to 4 and 5, we can get below weird result: --');
console.log(null < []);         // false
console.log(null > []);         // false
console.log(null == []);        // false
console.log(null <= []);        // true
console.log(null >= []);        // true

2

Di if (arr), selalu dievaluasi (ToBoolean) menjadi true jika arr adalah objek karena semua objek dalam JavaScript benar . (null bukan objek!)

[] == falsedievaluasi dalam pendekatan iteratif. Pada awalnya, jika satu sisi ==adalah primitif dan yang lainnya adalah objek, ia mengubah objek menjadi primitif pada awalnya, kemudian mengubah kedua sisi menjadi Angka jika kedua sisi tidak string(perbandingan string digunakan jika kedua sisi adalah string). Jadi perbandingannya adalah iterasi seperti, [] == false-> '' == false-> 0 == 0-> true.


2

Contoh:

const array = []
const boolValueOfArray = !!array // true

Itu terjadi karena

ToNumber(ToPrimitive([])) == ToNumber(false)  
  1. []adalah Arrayobjek kosong → ToPrimitive([])→ "" → ToNumber("")0
  2. ToNumber(false) → 0
  3. 0 == 0 → benar

1

Array dengan elemen (terlepas dari apakah 0, false atau array kosong lainnya), selalu memutuskan untuk truemenggunakan Perbandingan Kesetaraan Abstrak ==.

1. [] == false; // true, because an empty array has nothing to be truthy about
2. [2] == false; // false because it has at least 1 item
3. [false] == false; // also false because false is still an item
4. [[]] == false; // false, empty array is still an item

Tetapi menggunakan Pembandingan Kesetaraan Ketat ===, Anda mencoba untuk mengevaluasi konten variabel serta tipe datanya, itulah sebabnya:

1. [] === false; // false, because an array (regardless of empty or not) is not strictly comparable to boolean `false`
2. [] === true; // false, same as above, cannot strictly compare [] to boolean `true`
3. [[]] === false; // true, because see #1

-1

Anda dapat mengosongkan Array JavaScript dengan mereferensikannya ke array baru, menggunakan list = []atau menghapus elemen-elemen dari array yang saat ini direferensikan list.length = 0.

Sumber: Array Kosong JavaScript


-2

Tidak satu pun di atas membantu saya, ketika mencoba menggunakan plugin pemetaan knockout.js, mungkin karena "array kosong" tidak benar-benar kosong.

Saya akhirnya menggunakan: data-bind="if: arr().length"yang melakukan trik.

Ini khusus untuk sistem gugur, bukan pertanyaan OP, tapi mungkin itu akan membantu orang lain menjelajah di sini dalam situasi yang sama.


Jawaban ini tidak berhubungan
fauverisme

hanya tangensial, seperti yang
disangkal
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.