Як професійний математик я бачу в операторі однаковості Явскрипта ==
(його також називають "абстрактним порівнянням", "вільною рівністю" ) спробою побудувати відношення еквівалентності між сутностями, що включає рефлексивність , симетричність та перехідність . На жаль, два з цих трьох основних властивостей не спрацьовують:
A == A
може бути помилковим, наприклад
NaN == NaN // false
A == B
і B == C
разом не мають на увазі A == C
, напр
'1' == 1 // true
1 == '01' // true
'1' == '01' // false
Виживає лише симетрична властивість:
A == B
мається на увазі B == A
, яке порушення, мабуть, немислиме в будь-якому випадку і призведе до серйозного заколоту;)
Чому стосунки еквівалентності мають значення?
Тому що це найважливіший і найпоширеніший тип відносин, підтримуваний численними прикладами та додатками. Найважливіше застосування - це розкладання сутностей на класи еквівалентності , що саме по собі є дуже зручним та інтуїтивним способом розуміння відносин. А нееквівалентність призводить до відсутності класів еквівалентності, що, в свою чергу, призводить до недостатньої інтуїтивності та непотрібної складності, яка добре відома.
Чому так страшна ідея писати ==
за відношення нееквівалентності?
Тому що це порушує наше знайомство та інтуїцію, оскільки буквально будь-яке цікаве відношення подібності, рівності, конгруентності, ізоморфізму, ідентичності тощо є рівнозначністю.
Перетворення типів
Замість того, щоб покладатися на інтуїтивну еквівалентність, JavaScript вводить перетворення типів:
Оператор рівності перетворює операнди, якщо вони не одного типу, тоді застосовує суворе порівняння.
Але як визначається перетворення типу? Через набір складних правил з численними винятками?
Спроба побудувати відношення еквівалентності
Булеви. Ясна річ true
і false
не однакові і повинні бути в різних класах.
Числа. На щастя, рівність чисел уже чітко визначена, коли два різних числа ніколи не знаходяться в одному класі еквівалентності. У математиці, тобто. В JavaScript поняття числа має кілька деформована через присутність більш екзотичне -0
, Infinity
і -Infinity
. Наша математична інтуїція диктує, що 0
і -0
слід бути в одному класі (насправді -0 === 0
є true
), тоді як кожна з нескінченностей - це окремий клас.
Числа та булеви. З огляду на числові класи, куди ми ставимо булі? false
стає схожим на 0
, тоді як true
стає подібним до, 1
але жодного іншого числа:
true == 1 // true
true == 2 // false
Чи є логіка тут , щоб покласти true
разом з 1
? Слід визнати 1
, але так і є -1
. Я особисто не бачу причин для того, щоб true
звертатися 1
.
І стає ще гірше:
true + 2 // 3
true - 1 // 0
Тож true
справді перетворене 1
серед усіх чисел! Це логічно? Це інтуїтивно? Відповідь залишається як вправа;)
А як щодо цього:
1 && true // true
2 && true // true
Єдине логічне x
з x && true
істотою true
є x = true
. Що доводить, що 1
і 2
(і будь-яке інше число, ніж 0
) перетворюються на true
! Що це показує, це те, що наша конверсія не має іншого важливого властивості - біекція . Це означає, що дві різні сутності можуть конвертувати в одну. Що саме по собі не повинно бути великою проблемою. Велика проблема виникає, коли ми використовуємо це перетворення для опису співвідношення "однаковості" або "вільної рівності" того, що ми хочемо назвати. Але одне зрозуміло - це не буде відношенням еквівалентності, і це не буде інтуїтивно описано через класи еквівалентності.
Але чи можемо ми зробити краще?
Принаймні математично - точно так! Просте відношення еквівалентності між булевими числами та числами можна було б побудувати лише з одним класом false
та 0
бути в ньому. Так false == 0
було б єдиною нетривіальною вільною рівністю.
Що про струни?
Ми можемо обрізати рядки з пробілів на початку та в кінці для перетворення в числа, також ми можемо ігнорувати нулі попереду:
' 000 ' == 0 // true
' 0010 ' == 10 // true
Таким чином ми отримуємо просте правило для рядка - обрізаємо пробіли та нулі спереду. Або ми отримуємо число або порожній рядок, і в цьому випадку перетворюємо на це число або нуль. Або ми не отримуємо число, і в такому випадку ми не конвертуємо і не отримуємо нового відношення.
Таким чином ми могли фактично отримати ідеальне відношення еквівалентності до загального набору булей, чисел та рядків! За винятком того, що ... дизайнери JavaScript, очевидно, мають іншу думку:
' ' == '' // false
Тож дві струни, в які обидва перетворюються 0
, раптом не схожі! Чому чи чому? За правилом, рядки вільно рівні, коли вони суворо рівні! Це правило не тільки порушує транзитивність, як ми бачимо, але й вона є зайвою! Який сенс створити іншого оператора, ==
щоб він був суто ідентичним іншому ===
?
Висновок
Оператор вільної рівності ==
міг би бути дуже корисним, якби він дотримувався деяких основних математичних законів. Але, як це не сумно, корисність страждає.