Apa perbedaan antara (NaN! = NaN) dan (NaN! == NaN)?

148

Pertama-tama saya ingin menyebutkan bahwa saya tahu caranya isNaN()dan Number.isNaN()bekerja. Saya membaca The Definite Guide oleh David Flanagan dan dia memberikan contoh bagaimana memeriksa apakah nilainya NaN:

x !== x

Hal ini akan mengakibatkan truejika dan hanya jika xisNaN .

Tapi sekarang saya punya pertanyaan: mengapa dia menggunakan perbandingan yang ketat? Karena sepertinya begitu

x != x

berperilaku dengan cara yang sama. Apakah aman menggunakan kedua versi, atau saya kehilangan beberapa nilai dalam JavaScript yang akan kembali trueuntuk x !== xdan falseuntuk x != x?

Giorgi Nakeuri
sumber
10
Bisa jadi Flanagan lebih suka !==cek daripada !=cek. Sejauh yang saya ketahui tidak ada nilai lain di mana x != x. Tetapi ada dua kelompok pengembang JavaScript yang berbeda: mereka yang lebih suka !=dan yang lebih suka !==, baik untuk kecepatan, kejelasan, ekspresi, dll.
Steve Klösters
30
Mengapa menggunakan perbandingan longgar ketika perbandingan ketat berperilaku dengan cara yang sama?
Ry-
3
@ Raulucco: NaNbukan tipe yang unik, ini nomor. Ini adalah nilai unik yang tidak sama dengan dirinya sendiri.
TJ Crowder
8
Judulnya sepertinya menyesatkan orang. Saya sarankan mengubahnya menjadi sesuatu seperti "Apakah x! = X pernah berbeda dari x! == x?"
TJ Crowder
6
@femmestem: Giorgi berkata "dalam hal ini" itu masalah gaya. Dan dia benar dalam hal itu. Hal ini tidak gaya ketika jenis operan yang berbeda, tetapi adalah gaya ketika mereka sama. Secara terpisah: Flanagan melakukan perbandingan ===dengan NaN untuk menegaskan bahwa NaN tidak sama dengan dirinya sendiri. Dia tidak "salah," dia melakukannya sebagai latihan mengajar, menunjukkan bahwa itu tidak berhasil.
TJ Crowder

Jawaban:

128

Pertama, izinkan saya menunjukkan bahwa itu NaNadalah nilai yang sangat istimewa: Menurut definisi, itu tidak sama dengan dirinya sendiri. Itu berasal dari standar IEEE-754 yang digunakan angka JavaScript. Nilai "bukan angka" tidak pernah sama dengan nilai itu sendiri, bahkan ketika bitnya sama persis. (Yang tidak harus dalam IEEE-754, ini memungkinkan untuk beberapa nilai "bukan angka" yang berbeda.) Itulah mengapa ini bahkan muncul; semua nilai lain dalam JavaScript sama dengan dirinya sendiri, NaNhanya istimewa.

... apakah saya kehilangan beberapa nilai dalam JavaScript yang akan mengembalikan true untuk x! == x dan false untuk x! = x?

Tidak, bukan kau. Satu-satunya perbedaan antara !==dan !=adalah bahwa yang terakhir akan melakukan tipe paksaan jika perlu untuk mendapatkan jenis operan yang sama. Pada x != x, jenis operan adalah sama, dan itu persis sama dengan x !== x.

Ini jelas dari awal definisi Operasi Kesetaraan Abstrak :

  1. ReturnIfAbrupt (x).
  2. ReturnIfAbrupt (y).
  3. Jika Tipe (x) sama dengan Tipe (y), maka

    Kembalikan hasil melakukan Perbandingan Kesetaraan Ketat x === y.

  4. ...

Dua langkah pertama adalah pipa ledeng dasar. Jadi sebenarnya, langkah pertama ==adalah untuk melihat apakah tipenya sama dan, jika demikian, lakukan ===saja. !=dan !==hanya versi negated dari itu.

Jadi jika Flanagan benar bahwa hanya NaNakan memberikan yang benar untuk x !== x, kita dapat yakin bahwa itu juga benar bahwa hanya NaNakan memberikan yang benar untuk x != x.

Banyak programmer JavaScript yang secara default menggunakan ===dan !==menghindari beberapa jebakan di sekitar tipe paksaan yang dilakukan operator longgar, tetapi tidak ada yang bisa dibaca dalam penggunaan Flanagan terhadap operator ketat dan longgar dalam hal ini.

TJ Crowder
sumber
Saya memiliki 4.9.1 - Equality and Inequality Operatorsbagian membaca ulang dan ini tampaknya menjadi jawabannya. Poin kunci untuk=== perbandingan adalah: If the two values have the same type, test them for strict equality as described above. If they are strictly equal, they are equal. If they are not strictly equal, they are not equal.
Giorgi Nakeuri
@GiorgiNakeuri: Saya tidak yakin apa yang Anda maksud dengan 4.9.1, mungkin buku Flanagan? Tapi itu pada dasarnya mengatakan apa yang dikatakan kutipan dari spesifikasi di atas, ya.
TJ Crowder
2
Saya menerima ini karena ini menjawab pertanyaan saya dengan cara formal dan tepat. Terima kasih atas penjelasannya!
Giorgi Nakeuri
1
@ Moshe: Apa yang Anda maksud dengan "binding hidup"? (Istilah tidak muncul dalam spesifikasi.) Apakah maksud Anda seperti contoh GOTO 0 di mana asebenarnya merupakan fungsi dan tidak mengembalikan nilai yang sama dua kali? Itu bukan hal yang sama dengan nilai yang !==akan benar, itulah yang ditanyakan OP. Itu hanya fungsi yang mengembalikan nilai yang berbeda. foo() !== foo()tidak harus benar juga, karena foomungkin mengembalikan nilai yang berbeda pada setiap panggilan.
TJ Crowder
1
@ Moshe Nah itu cara super-jahat untuk mengacaukan properti dan getter. Tapi itu tampaknya hampir sama dengan contoh GOTO 0, hanya dengan lapisan tipuan ekstra.
JAB
37

Untuk keperluan NaN, !=dan !==lakukan hal yang sama.

Namun, banyak programmer menghindari ==atau !=menggunakan JavaScript. Misalnya, Douglas Crockford menganggap mereka sebagai " bagian buruk " dari bahasa JavaScript karena mereka berperilaku dengan cara yang tidak terduga dan membingungkan:

JavaScript memiliki dua set operator kesetaraan: ===dan !==, dan si kembar jahat ==dan !=. Yang bagus bekerja seperti yang Anda harapkan.

... Saran saya adalah jangan pernah menggunakan si kembar jahat. Sebaliknya, selalu gunakan=== dan !==.

jkdev
sumber
2
Pertanyaannya bukan tentang NaN (terlepas dari judulnya). Pertanyaannya adalah "apakah saya kehilangan beberapa nilai dalam JavaScript yang akan mengembalikan true untuk x! == x dan false untuk x! = X?"
TJ Crowder
@TJCrowder Dua pertanyaan, sungguh. Pertanyaan pertama adalah "Apakah aman menggunakan kedua versi" dan jawabannya adalah bahwa kedua versi itu setara. Saya suka jawaban "under-the-hood" Anda yang menjelaskan semuanya secara terperinci.
jkdev
22

Hanya untuk bersenang-senang, izinkan saya menunjukkan kepada Anda contoh buatan di mana xtidak ada NaNtetapi operator berperilaku berbeda pula. Definisi pertama:

Object.defineProperty(
  self,
  'x',
  { get: function() { return self.y = self.y ? 0 : '0'; } }
);

Lalu kita punya

x != x // false

tapi

x !== x // true
GOTO 0
sumber
9
Ha! :-) Tapi itu efektif di foo() != foo()mana foo mengembalikan 1 lalu 2. Misalnya, nilainya tidak sama, hanya membandingkan nilai yang berbeda.
TJ Crowder
2

Saya hanya ingin menunjukkan NaNbukan satu-satunya hal yang menghasilkan x !== xtanpa menggunakan objek global. Ada banyak cara pintar untuk memicu perilaku ini. Berikut ini salah satu yang menggunakan getter:

var i = 0, obj = { get x() { return i++; }};
with(obj) // force dynamic context, this is evil. 
console.log(x === x); // false

Sebagai jawaban lain menunjukkan, ==melakukan jenis koersi, tetapi dalam bahasa lain dan par standar - NaN menunjukkan kegagalan perhitungan, dan untuk alasan yang baik tidak sama dengan itu sendiri.

Untuk beberapa alasan di luar saya orang menganggap ini masalah dengan JS tetapi kebanyakan bahasa yang memiliki dua kali lipat (yaitu, C, Java, C ++, C #, Python dan lain-lain) menunjukkan perilaku yang tepat ini dan orang-orang baik-baik saja dengan itu.

Benjamin Gruenbaum
sumber
2
Ya, itulah yang disebutkan oleh @TJCrowder dalam komentar untuk jawaban GOTO_0, bukan?
Giorgi Nakeuri
Bisakah Anda mengklarifikasi cara mendapatkan paksaan tipe ambigu dalam bahasa lain ini?
chicocvenancio
0

Seperti terkadang, gambar lebih baik daripada kata-kata, periksa tabel ini (yang merupakan alasan bagi saya untuk membuat ini sebagai jawaban, bukan komentar karena mendapat visibilitas yang lebih baik).

Di sana Anda dapat melihat bahwa perbandingan kesetaraan yang ketat (===) hanya mengembalikan true jika jenis dan konten cocok, maka

var f = "-1" === -1; //false

Sementara perbandingan kesetaraan abstrak (==) hanya memeriksa konten * dengan mengonversi jenis dan kemudian membandingkannya dengan ketat:

var t = "-1" == -1; //true

Meskipun tidak jelas, tanpa berkonsultasi dengan ECMA , apa yang dipertimbangkan JavaScript saat membandingkan, dengan cara yang dievaluasi oleh kode di bawah sebagai true.

 var howAmISupposedToKnowThat = [] == false; //true
MVCDS
sumber