Dalam JavaScript, Bagian Yang Baik , Douglas Crockford menulis:
JavaScript memiliki dua set operator kesetaraan:
===
dan!==
, dan si kembar jahat==
dan!=
. Yang bagus bekerja seperti yang Anda harapkan. Jika kedua operan memiliki tipe yang sama dan memiliki nilai yang sama, maka===
hasilkantrue
dan!==
hasilkanfalse
. Si kembar jahat melakukan hal yang benar ketika operan dari jenis yang sama, tetapi jika mereka dari jenis yang berbeda, mereka berusaha untuk memaksa nilai-nilai itu. Aturan-aturan yang dengannya mereka melakukan hal itu rumit dan tidak mudah diingat. Ini adalah beberapa kasus menarik:'' == '0' // false 0 == '' // true 0 == '0' // true false == 'false' // false false == '0' // true false == undefined // false false == null // false null == undefined // true ' \t\r\n ' == 0 // true
Kurangnya transitivitas mengkhawatirkan. Saran saya adalah jangan pernah menggunakan si kembar jahat. Sebaliknya, selalu gunakan
===
dan!==
. Semua perbandingan hanya menunjukkan produkfalse
dengan===
operator.
Dengan pengamatan yang tegas ini, apakah pernah ada waktu ketika menggunakan ==
sebenarnya cocok?
sumber
==
secara default,===
berdiri dan beri tahu saya sesuatu yang penting sedang terjadi.Jawaban:
Saya akan membuat argumen untuk
==
Douglas Crockford yang Anda kutip dikenal karena pendapatnya yang banyak dan seringkali sangat berguna. Sementara saya bersama Crockford dalam kasus khusus ini, layak disebut bukan satu - satunya pendapat. Ada yang lain seperti pencipta bahasa Brendan Eich yang tidak melihat masalah besar dengannya
==
. Argumennya sedikit seperti berikut:JavaScript adalah bahasa yang diketik * dengan perilaku. Segala sesuatu diperlakukan berdasarkan apa yang dapat mereka lakukan dan bukan tipe yang sebenarnya. Inilah sebabnya mengapa Anda dapat memanggil metode array
.map
pada NodeList atau pada set pilihan jQuery. Itu juga mengapa Anda dapat melakukan3 - "5"
dan mendapatkan sesuatu yang bermakna kembali - karena "5" dapat bertindak seperti angka.Saat Anda melakukan
==
kesetaraan, Anda membandingkan konten variabel dan bukan jenisnya . Berikut adalah beberapa kasus di mana ini berguna:.value
elemen input di DOM? Tidak masalah! Anda tidak harus mulai melemparkannya atau mengkhawatirkan tipenya - Anda bisa==
langsung memasukkannya ke angka dan mendapatkan sesuatu yang berarti kembali.== null
melakukannya karena secara perilakunull
mewakili tidak ada apa pun di sana dan tidak terdefinisi juga tidak memiliki apa pun di sana.==
argumen, itu akan memperlakukan kasus pengguna tidak memasukkan apa pun atau hanya ruang putih untuk Anda yang mungkin apa yang Anda butuhkan.Mari kita lihat contoh-contoh Crockford dan jelaskan secara perilaku:
Pada dasarnya,
==
dirancang untuk bekerja berdasarkan bagaimana primitif berperilaku dalam JavaScript tidak didasarkan pada apa yang mereka adalah . Meskipun saya pribadi tidak setuju dengan sudut pandang ini, pasti ada gunanya melakukannya - terutama jika Anda mengambil paradigma ini untuk memperlakukan tipe berdasarkan perilaku bahasa secara luas.* beberapa mungkin lebih suka mengetik struktural nama yang lebih umum tetapi ada perbedaan - tidak benar-benar tertarik membahas perbedaan di sini.
sumber
==
bukan karena tidak ada perbandingan yang berguna , aturannya mustahil untuk diingat sehingga Anda hampir dijamin untuk membuat kesalahan. Misalnya: "Perlu memeriksa apakah Anda mendapat input yang berarti dari pengguna?", Tetapi '0' adalah input yang bermakna dan'0'==false
benar. Jika Anda menggunakan===
Anda harus berpikir secara eksplisit tentang hal itu dan Anda tidak akan membuat kesalahan.3 - "5"
contoh menimbulkan titik baik sendiri: bahkan jika Anda secara eksklusif menggunakan===
untuk perbandingan, itu hanya cara variabel bekerja di Javascript. Tidak ada cara untuk menghindarinya sepenuhnya.==
kode saya sendiri, saya merasa ini anti-pola dan saya sepenuhnya setuju bahwa algoritma kesetaraan abstrak sulit untuk diingat. Apa yang saya lakukan di sini adalah membuat argumen yang dibuat orang untuk itu.Ternyata jQuery menggunakan konstruk
secara ekstensif, sebagai singkatan untuk kode yang setara:
Ini adalah konsekuensi dari Spesifikasi Bahasa ECMAScript § 11.9.3, Algoritma Perbandingan Kesetaraan Abstrak , yang menyatakan, antara lain, bahwa
dan
Teknik khusus ini cukup umum sehingga JSHint memiliki bendera yang dirancang khusus untuk itu.
sumber
== null or undefined
adalah satu-satunya tempat di mana saya tidak menggunakan===
atau!==
==
dan hanya digunakan===
dalam kasus-kasus di mana diperlukan: github.com/madrobby/zepto/blob/master/src/event.js__proto__
dan pada gilirannya memaksanya hampir satu per satu ke dalam spesifikasi bahasa untuk menghindari melanggar situs web seluler.Memeriksa nilai untuk
null
atauundefined
adalah satu hal, seperti yang telah dijelaskan secara melimpah.Ada hal lain, di mana
==
bersinar:Anda dapat mendefinisikan perbandingan dari
>=
seperti itu (orang biasanya mulai dari>
tetapi saya menemukan ini lebih elegan):a > b
<=>a >= b && !(b >= a)
a == b
<=>a >= b && b >= a
a < b
dana <= b
dibiarkan sebagai latihan untuk pembaca.Seperti yang kita ketahui, dalam JavaScript
"3" >= 3
dan"3" <= 3
, dari mana Anda dapatkan3 == "3"
. Anda dapat menegaskan bahwa ini adalah ide yang mengerikan untuk memungkinkan penerapan perbandingan antara string dan angka dengan menguraikan string. Tetapi mengingat bahwa ini adalah cara kerjanya,==
benar - benar cara yang tepat untuk mengimplementasikan hubungan operator itu.Jadi hal yang benar-benar baik
==
adalah konsisten dengan semua hubungan lain. Dengan kata lain, jika Anda menulis ini:Anda
==
sudah menggunakan secara implisit .Sekarang untuk pertanyaan yang cukup terkait: Apakah itu pilihan yang buruk untuk mengimplementasikan perbandingan angka dan string cara penerapannya? Terlihat dalam isolasi, tampaknya hal yang agak bodoh untuk dilakukan. Tetapi dalam konteks bagian lain dari JavaScript dan DOM, ini relatif pragmatis, mengingat:
Object
untuk memiliki peta yang jarang dari int ke nilai)input[type=number]
)Untuk sejumlah alasan, masuk akal untuk membuat string berperilaku seperti angka ketika dibutuhkan. Dan dengan asumsi bahwa perbandingan string dan penggabungan string memiliki operator yang berbeda (misalnya
::
untuk menyimpulkan dan metode untuk membandingkan (di mana Anda dapat menggunakan semua jenis parameter mengenai sensitivitas huruf dan apa yang tidak)), ini sebenarnya akan menjadi kurang berantakan. Tapi operator kelebihan ini mungkin sebenarnya dari mana "Java" dalam "JavaScript" berasal;)sumber
>=
tidak benar-benar transitif. Sangat mungkin di JS yang tidaka > b
ataua < b
tidakb == a
(misalnyaNaN
:).+
tidak benar-benar komutatif, karenaNaN + 5 == NaN + 5
tidak berlaku. Intinya adalah bahwa>=
bekerja dengan nilai-nilai angka yang==
bekerja secara konsisten. Seharusnya tidak mengejutkan bahwa "bukan angka" pada dasarnya bukan angka-ish;)==
konsisten dengan perilaku buruk>=
? Hebat, sekarang saya berharap ada>==
...>=
agak konsisten dengan sisa bahasa / API standar. Dalam totalitasnya, JavaScript mengelola lebih dari jumlah bagian uniknya. Jika Anda menginginkan>==
, apakah Anda juga menginginkan yang ketat+
? Dalam praktiknya, banyak dari keputusan ini membuat banyak hal menjadi lebih mudah. Jadi saya tidak akan terburu-buru menilai mereka miskin.>=
bermakna,==
sama-sama bermakna - itu saja. Tidak ada yang mengatakan Anda harus membandingkan[[]]
denganfalse
. Dalam bahasa seperti C hasil tingkat omong kosong ini adalah perilaku yang tidak terdefinisi. Perlakukan saja dengan cara yang sama: jangan lakukan itu. Dan kamu akan baik-baik saja. Anda juga tidak perlu mengingat aturan sihir apa pun. Dan kemudian itu sebenarnya agak lurus ke depan.Sebagai ahli matematika profesional, saya melihat dalam operator kesamaan Javscript
==
(juga disebut "perbandingan abstrak", "kesetaraan longgar" ) upaya untuk membangun hubungan ekivalensi antara entitas, yang termasuk menjadi refleksif , simetris , dan transitif . Sayangnya, dua dari tiga sifat dasar ini gagal:==
tidak refleksif :A == A
mungkin salah, mis==
tidak transitif :A == B
danB == C
bersama-sama tidak menyiratkanA == C
, misHanya properti simetrik yang bertahan:
A == B
menyiratkanB == A
, pelanggaran mana yang mungkin tidak terpikirkan dalam kasus apa pun dan akan menyebabkan pemberontakan yang serius;)Mengapa hubungan kesetaraan penting?
Karena itulah jenis hubungan yang paling penting dan lazim, didukung oleh banyak contoh dan aplikasi. Aplikasi yang paling penting adalah dekomposisi entitas menjadi kelas kesetaraan , yang dengan sendirinya merupakan cara yang sangat mudah dan intuitif untuk memahami hubungan. Dan kegagalan untuk menjadi ekuivalensi mengarah pada kurangnya kelas ekivalensi, yang pada gilirannya mengarah pada kurangnya intuisi dan kompleksitas yang tidak perlu yang terkenal.
Mengapa ini merupakan ide yang mengerikan untuk menulis
==
untuk hubungan yang tidak setara?Karena itu merusak keakraban dan intuisi kita, karena secara harfiah ada hubungan yang menarik dari kesamaan, kesetaraan, kongruensi, isomorfisme, identitas dll adalah kesetaraan.
Jenis konversi
Alih-alih mengandalkan kesetaraan intuitif, JavaScript memperkenalkan konversi jenis:
Tetapi bagaimana definisi jenis konversi? Melalui serangkaian aturan rumit dengan banyak pengecualian?
Mencoba membangun relasi kesetaraan
Boolean. Jelas
true
danfalse
tidak sama dan harus di kelas yang berbeda.Angka Untungnya, kesetaraan angka sudah didefinisikan dengan baik, di mana dua angka yang berbeda tidak pernah berada dalam kelas ekivalensi yang sama. Dalam matematika, itu. Dalam JavaScript gagasan angka agak berubah bentuk melalui kehadiran yang lebih eksotis
-0
,Infinity
dan-Infinity
. Intuisi matematika kami menyatakan bahwa0
dan-0
harus di kelas yang sama (pada kenyataannya-0 === 0
adalahtrue
), sedangkan masing-masing infinities adalah kelas terpisah.Bilangan dan Boolean. Mengingat nomor kelas, di mana kita meletakkan boolean?
false
menjadi mirip dengan0
, sedangkantrue
menjadi serupa1
tetapi tidak ada nomor lain:Apakah ada logika di sini untuk
true
disatukan1
? Diakui1
dibedakan, tetapi begitu juga-1
. Saya pribadi tidak melihat alasan untuk mengkonversitrue
ke1
.Dan itu menjadi lebih buruk:
Jadi
true
memang diubah menjadi di1
antara semua angka! Apakah ini logis? Apakah ini intuitif? Jawabannya dibiarkan sebagai latihan;)Tapi bagaimana dengan ini:
Satu-satunya boolean
x
denganx && true
makhluktrue
adalahx = true
. Yang membuktikan bahwa keduanya1
dan2
(dan nomor lain selain0
) berkonversi ketrue
! Apa yang ditunjukkannya adalah bahwa konversi kami gagal dalam properti penting lainnya - yaitu penambangan . Berarti bahwa dua entitas yang berbeda dapat dikonversi ke entitas yang sama. Yang dengan sendirinya tidak harus menjadi masalah besar. Masalah besar muncul ketika kita menggunakan konversi ini untuk menggambarkan hubungan "kesamaan" atau "kesetaraan longgar" dari apa pun yang kita ingin menyebutnya. Tapi satu hal yang jelas - itu tidak akan menjadi hubungan ekivalensi dan tidak akan dijelaskan secara intuitif melalui kelas ekivalensi.Tetapi bisakah kita melakukan yang lebih baik?
Setidaknya secara matematis - pasti ya! Hubungan kesetaraan sederhana antara boolean dan angka hanya dapat dibangun dengan
false
dan0
berada di kelas yang sama. Jadifalse == 0
akan menjadi satu-satunya kesetaraan longgar non-sepele.Bagaimana dengan string?
Kita dapat memangkas string dari spasi putih di awal dan akhir untuk mengkonversi ke angka, juga kita bisa mengabaikan nol di depan:
Jadi kita mendapatkan aturan sederhana untuk string - trim spasi putih dan nol di depan. Entah kita mendapatkan angka atau string kosong, dalam hal ini kita mengkonversi ke angka itu atau nol. Atau kami tidak mendapatkan nomor, dalam hal ini kami tidak mengonversi sehingga tidak ada hubungan baru.
Dengan cara ini kita benar-benar bisa mendapatkan hubungan kesetaraan yang sempurna pada set boolean, angka, dan string! Kecuali itu ... Desainer JavaScript jelas memiliki pendapat lain:
Jadi dua string yang keduanya dikonversi menjadi
0
tiba-tiba tidak serupa! Kenapa atau mengapa? Menurut aturan, string secara longgar sama persis ketika mereka benar-benar sama! Tidak hanya aturan ini memecah transitivitas seperti yang kita lihat, tetapi juga berlebihan! Apa gunanya membuat operator lain==
agar benar-benar identik dengan yang lain===
?Kesimpulan
Operator kesetaraan longgar
==
bisa sangat berguna jika mematuhi beberapa hukum matematika dasar. Tapi sayangnya tidak ada manfaatnya.sumber
NaN
? Selain itu, kecuali jika format angka tertentu diberlakukan untuk perbandingan dengan string, perbandingan string yang tidak intuitif atau non-transitivitas harus dihasilkan.NaN
bertindak sebagai warga negara yang buruk :-). Transivity dapat dan harus ditegakkan untuk perbandingan kesetaraan, intuitif atau tidak.Ya, saya telah menemukan kasus penggunaan untuk itu, yaitu ketika Anda membandingkan kunci dengan nilai numerik:
Saya pikir itu jauh lebih alami untuk melakukan perbandingan
key == some_number
daripada sebagaiNumber(key) === some_number
atau sebagaikey === String(some_number)
.sumber
Saya menemukan aplikasi yang sangat berguna hari ini. Jika Anda ingin membandingkan angka empuk, seperti
01
bilangan bulat normal,==
berfungsi dengan baik. Sebagai contoh:Ini menghemat Anda menghapus 0 dan mengkonversi ke integer.
sumber
'04'-0 === 4
, atau mungkinparseInt('04', 10) === 4
+'01' === 1
'011' == 011 // false
dalam mode non-ketat, dan SyntaxError dalam mode ketat. :)Saya tahu ini adalah jawaban yang terlambat, tetapi tampaknya ada beberapa kemungkinan kebingungan tentang
null
danundefined
, yang IMHO adalah yang membuat==
kejahatan, lebih dari kurangnya transitivitas, yang cukup buruk. Mempertimbangkan:Apa artinya ini?
p1
memiliki penyelia yang bernama "Alice."p2
memiliki penyelia yang namanya "Tidak Ada."p3
secara eksplisit, tegas, tidak memiliki pengawas .p4
mungkin atau mungkin memiliki penyelia. Kita tidak tahu, kita tidak peduli, kita tidak seharusnya tahu (masalah privasi?), Karena itu bukan urusan kita.Saat Anda menggunakan
==
Anda sedang melakukan conflatingnull
danundefined
mana yang sepenuhnya tidak tepat. Kedua istilah tersebut memiliki arti yang sangat berbeda! Mengatakan bahwa saya tidak memiliki penyelia hanya karena saya menolak memberi tahu Anda siapa penyelia saya yang salah!Saya mengerti ada programmer yang tidak peduli tentang perbedaan antara
null
danundefined
atau memilih untuk menggunakan istilah ini secara berbeda. Dan jika dunia Anda tidak menggunakannull
danundefined
dengan benar, atau Anda ingin memberikan interpretasi Anda sendiri untuk istilah-istilah ini, maka jadilah itu. Saya pikir itu bukan ide yang bagus.Ngomong-ngomong, aku tidak punya masalah dengan
null
danundefined
keduanya menjadi palsu! Tidak apa-apa untuk mengatakannyadan kemudian
null
danundefined
akan menyebabkan kode yang memproses pengawas dilewati. Itu benar, karena kita tidak tahu atau tidak memiliki penyelia. Semuanya bagus. Tetapi kedua situasi itu tidak sama . Inilah sebabnya mengapa==
salah. Sekali lagi, hal-hal dapat menjadi palsu dan digunakan dalam arti mengetik bebek, yang sangat bagus untuk bahasa yang dinamis. Ini adalah JavaScript yang tepat, Pythonic, Rubyish, dll. Tetapi sekali lagi, hal-hal ini TIDAK setara.Dan jangan mulai pada non-transitivitas:
"0x16" == 10
,10 == "10"
tapi tidak"10" == "0x16"
. Ya, JavaScript jenisnya lemah. Ya, itu pemaksaan. Tapi paksaan seharusnya tidak pernah berlaku untuk kesetaraan.Omong-omong, Crockford memang memiliki pendapat yang kuat. Tapi tahukah Anda? Dia benar di sini!
FWIW Saya mengerti bahwa ada, dan secara pribadi saya mengalami situasi di mana
==
nyaman! Seperti mengambil input string untuk angka dan, katakanlah, membandingkan dengan 0. Namun, ini adalah hack. Anda memiliki kemudahan sebagai tradeoff untuk model dunia yang tidak akurat.TL; DR: kepalsuan adalah konsep yang hebat. Seharusnya tidak mencakup kesetaraan.
sumber
p5
... satu situasi ditypeof(p5.supervisor) === typeof(undefined)
mana pengawas bahkan tidak ada sebagai konsep: D