В хороших частях JavaScript Дуглас Крокфорд писал:
В JavaScript есть два набора операторов равенства:
===
и!==
, и их злые близнецы==
и!=
. Хорошие работают так, как вы ожидаете. Если два операнда имеют одинаковый тип и имеют одинаковое значение, то===
производитtrue
и!==
производитfalse
. Злые близнецы поступают правильно, когда операнды относятся к одному и тому же типу, но если они относятся к разным типам, они пытаются привести значения. Правила, по которым они это делают, сложны и не запоминаются. Вот некоторые из интересных случаев:'' == '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
Отсутствие транзитивности вызывает тревогу. Мой совет - никогда не использовать злых близнецов. Вместо этого всегда используйте
===
и!==
. Все приведенные сравнения производятсяfalse
с===
оператором.
Учитывая это однозначное наблюдение, было ли когда-нибудь время, когда использование ==
могло бы быть действительно уместным?
источник
==
по умолчанию,===
выделяется и дает мне знать, что происходит что-то важное.Ответы:
Я собираюсь привести аргумент в пользу
==
Дуглас Крокфорд, которого вы цитировали, известен своими многочисленными и часто очень полезными мнениями. Хотя в данном конкретном случае я с Крокфордом, стоит упомянуть, что это не единственное мнение. Есть и другие, такие как создатель языка Брендан Айх, которые не видят большой проблемы с этим
==
. Аргумент выглядит примерно так:JavaScript является поведенчески * типизированным языком. Вещи рассматриваются на основе того, что они могут сделать, а не их фактический тип. Вот почему вы можете вызывать
.map
метод массива в NodeList или в наборе выбора jQuery. Это также, почему вы можете сделать3 - "5"
и получить что-то значимое обратно - потому что «5» может действовать как число.Когда вы выполняете
==
равенство, вы сравниваете содержимое переменной, а не ее тип . Вот несколько случаев, когда это полезно:.value
элемент ввода в DOM? Нет проблем! Вам не нужно начинать разыгрывать его или беспокоиться о его типе - вы можете==
сразу получить цифры и получить что-то значимое обратно.== null
это, поскольку поведенческиnull
представляете, что там ничего нет, а у undefined там тоже ничего нет.==
аргументом, он будет обрабатывать случаи, когда пользователь ничего не вводил или просто пробел для вас, что, вероятно, то, что вам нужно.Давайте посмотрим на примеры Крокфорда и объясним их поведение:
По сути,
==
предназначен для работы на основе того, как примитивы ведут себя в JavaScript, а не на основе того, чем они являются . Хотя я лично не согласен с этой точкой зрения, в этом определенно есть смысл, особенно если вы берете эту парадигму обработки типов, основанную на поведенческом поведении.* некоторые могут предпочесть структурную типизацию имени, которая более распространена, но есть разница - не очень интересно обсуждать эту разницу здесь.
источник
==
не в том, что ни одно из его сравнений не является полезным , а в том, что правила невозможно запомнить, поэтому вы почти гарантированно допустите ошибки. Например: «Нужно проверить, получили ли вы значимый ввод от пользователя?», Но «0» является значимым вводом и'0'==false
является истинным. Если бы вы использовали,===
вам пришлось бы об этом подумать, и вы бы не ошиблись.3 - "5"
Пример поднимает хорошую точку на его собственном: даже если использовать исключительно===
для сравнения, что это именно так , как переменные работают в JavaScript. Нет никакого способа избежать этого полностью.==
в своем собственном коде, я нахожу это анти-паттерном и полностью согласен с тем, что алгоритм абстрактного равенства трудно запомнить. То, что я делаю здесь, - это аргумент, который люди приводят за этоОказывается, что jQuery использует конструкцию
широко, как сокращение для эквивалентного кода:
Это является следствием спецификации языка ECMAScript § 11.9.3, Алгоритм сравнения абстрактного равенства , который, среди прочего, гласит, что
а также
Эта конкретная техника достаточно распространена, и JSHint имеет специально разработанный для нее флаг .
источник
== null or undefined
- это единственное место, где я не пользуюсь===
или!==
==
и используется только===
в тех случаях, когда это необходимо: github.com/madrobby/zepto/blob/master/src/event.js__proto__
и почти в одиночку вводит ее в спецификацию языка, чтобы избежать взлома мобильных веб-сайтов.Проверка значений для
null
илиundefined
это одна вещь, как было обильно объяснено.Есть еще одна вещь, где
==
светит:Вы можете определить сравнение
>=
примерно так (люди обычно начинают с этого,>
но я считаю это более элегантным):a > b
<=>a >= b && !(b >= a)
a == b
<=>a >= b && b >= a
a < b
иa <= b
оставлены в качестве упражнения для читателя.Как известно, в JavaScript
"3" >= 3
и"3" <= 3
откуда вы получаете3 == "3"
. Вы можете заметить, что это ужасная идея - разрешать сравнение строк и чисел, анализируя строку. Но учитывая , что это так , как это работает,==
абсолютно правильный путь для реализации этого оператора отношения.Так что действительно хорошо то,
==
что это согласуется со всеми другими отношениями. Другими словами, если вы напишите это:Вы уже неявно используете
==
.Теперь к довольно связанному вопросу: был ли плохой выбор осуществлять сравнение чисел и строк так, как оно реализовано? В одиночестве это выглядит довольно глупо. Но в контексте других частей JavaScript и DOM это относительно прагматично, учитывая, что:
Object
разреженную карту от целочисленных значений до значений)input[type=number]
)По целому ряду причин имеет смысл заставить строки вести себя как числа, когда это необходимо. И если предположить, что сравнение строк и конкатенация строк имеют разные операторы (например,
::
для конкатенации и метод сравнения (где вы можете использовать все виды параметров, касающихся чувствительности к регистру, а что нет)), на самом деле это будет меньше беспорядка. Но эта перегрузка оператора, вероятно, на самом деле происходит от "Java" в "JavaScript";)источник
>=
это не совсем переходный процесс. В JS вполне возможно, что ни,a > b
ни,a < b
ниb == a
(например:)NaN
.+
что на самом деле не коммутативно, потому чтоNaN + 5 == NaN + 5
не имеет места. Дело в том, что>=
работает со значениями нумерации, для которых==
работает согласованно. Не должно быть удивительно, что "не число" по своей природе не число - число;)==
соответствует плохому поведению>=
? Отлично, теперь я хотел бы, чтобы>==
...>=
довольно согласуется с остальными языковыми / стандартными API. В целом, JavaScript умудряется быть больше, чем сумма его причудливых частей. Если вы хотите>==
, вы также хотите строгий+
? На практике многие из этих решений значительно облегчают жизнь. Так что я бы не поспешил судить их как бедных.>=
имеет значение,==
одинаково значимым - и все. Никто не говорит, что вы должны сравнить[[]]
сfalse
. В таких языках, как C, результатом такого уровня глупости является неопределенное поведение. Просто относитесь к этому так же: не делайте этого. И у тебя все будет хорошо. Вам также не нужно будет помнить какие-либо магические правила. И тогда это на самом деле довольно прямо вперед.Как профессиональный математик, я вижу в операторе одинаковости Javscript
==
(также называемом «абстрактное сравнение», «слабое равенство» ) попытку построить отношение эквивалентности между сущностями, которое включает в себя рефлексивность , симметрию и переходность . К сожалению, два из этих трех основных свойств не работают:==
не рефлексивно :A == A
может быть ложным, например==
не является переходным :A == B
иB == C
вместе не подразумеваютA == C
, например,Выживает только симметричное свойство:
A == B
подразумеваетB == A
, что нарушение, вероятно, немыслимо в любом случае и приведет к серьезному восстанию;)Почему отношения эквивалентности имеют значение?
Потому что это наиболее важный и распространенный тип отношений, поддерживаемый многочисленными примерами и приложениями. Наиболее важным применением является разложение сущностей на классы эквивалентности , что само по себе является очень удобным и интуитивно понятным способом понимания отношений. А отсутствие эквивалентности приводит к отсутствию классов эквивалентности, что, в свою очередь, приводит к отсутствию интуитивности и ненужной сложности, которая хорошо известна.
Почему такая ужасная идея писать
==
для отношения неэквивалентности?Потому что это нарушает наше знакомство и интуицию, так как буквально любое интересное отношение сходства, равенства, конгруэнтности, изоморфизма, идентичности и т. Д. Является эквивалентностью.
Тип преобразования
Вместо того, чтобы полагаться на интуитивную эквивалентность, JavaScript вводит преобразование типов:
Но как определяется преобразование типов? Через набор сложных правил с многочисленными исключениями?
Попытка построить отношение эквивалентности
Булевы. Понятно
true
иfalse
не одно и то же и должно быть в разных классах.Числа. К счастью, равенство чисел уже четко определено, в котором два разных числа никогда не находятся в одном классе эквивалентности. В математике это так. В JavaScript понятие числа несколько искажается из-за присутствия более экзотического
-0
,Infinity
и-Infinity
. Наша математическая интуиция подсказывает , что0
и-0
должно быть в том же классе (на самом деле-0 === 0
являетсяtrue
), в то время как каждый из бесконечностей представляет собой отдельный класс.Числа и логические значения. Учитывая количество классов, где мы можем поставить логические значения?
false
становится похожим на0
, тогда какtrue
становится похожим на1
другое число:Есть ли логика здесь , чтобы положить
true
вместе с1
? По общему признанию1
различается, но это так-1
. Я лично не вижу смысла переходитьtrue
на1
.И это становится еще хуже:
Так
true
действительно превращается в1
число! Это логично? Это интуитивно понятно? Ответ оставлен как упражнение;)Но как насчет этого:
Единственное логическое
x
сx && true
существомtrue
этоx = true
. Что доказывает, что оба1
и2
(и любое другое число0
) конвертируются вtrue
! То, что он показывает, - это то, что наше обращение не соответствует другому важному свойству - биекции . Это означает, что две разные сущности могут конвертировать в одну и ту же. Что само по себе не должно быть большой проблемой. Большая проблема возникает, когда мы используем это преобразование, чтобы описать отношение «одинаковости» или «свободного равенства» того, что мы хотим назвать. Но ясно одно - оно не будет отношением эквивалентности и не будет интуитивно описываться через классы эквивалентности.Но можем ли мы сделать лучше?
По крайней мере, математически - определенно да! Простое отношение эквивалентности между булевыми и числами может быть построено только с
false
и0
быть в том же классе. Такfalse == 0
что было бы единственным нетривиальным свободным равенством.А как насчет строк?
Мы можем обрезать строки из пробелов в начале и конце для преобразования в числа, также мы можем игнорировать нули перед:
Таким образом, мы получаем простое правило для строки - обрезаем пробелы и нули впереди. Либо мы получим число, либо пустую строку, и в этом случае мы конвертируем в это число или ноль. Или мы не получаем число, в этом случае мы не конвертируем и поэтому не получаем новых отношений.
Таким образом, мы могли бы получить идеальное отношение эквивалентности для всего набора логических значений, чисел и строк! Кроме этого ... У разработчиков JavaScript, очевидно, другое мнение:
Таким образом, две строки, в которые оба преобразуются
0
, неожиданно не похожи! Почему или почему? Согласно правилу, строки практически равны, когда они строго равны! Как мы видим, не только это правило нарушает транзитивность, но и избыточно! Какой смысл создавать другого оператора,==
чтобы он был строго идентичен другому===
?Заключение
Оператор свободного равенства
==
мог бы быть очень полезным, если бы он соблюдал некоторые фундаментальные математические законы. Но, как это ни печально, его полезность страдает.источник
NaN
? Кроме того, если для сравнения со строками не установлен конкретный числовой формат, должно произойти либо неинтуитивное сравнение строк, либо непереходность.NaN
действует как плохой гражданин :-). Транзитивность может и должна поддерживаться для любого сравнения эквивалентности, интуитивно понятного или нет.Да, я наткнулся на вариант использования, а именно, когда вы сравниваете ключ с числовым значением:
Я думаю, что гораздо более естественно выполнять сравнение,
key == some_number
а не какNumber(key) === some_number
или какkey === String(some_number)
.источник
Я наткнулся на довольно полезное приложение сегодня. Если вы хотите сравнить дополненные числа, как
01
с обычными целыми числами,==
работает просто отлично. Например:Это спасает вас от удаления 0 и преобразования в целое число.
источник
'04'-0 === 4
, или, возможно,parseInt('04', 10) === 4
+'01' === 1
'011' == 011 // false
в нестрогом режиме и SyntaxError в строгом режиме. :)Я знаю , что это поздний ответ, но там , кажется, некоторая возможная неразбериха
null
иundefined
, что ИМХО это то , что делает==
зло, тем более , что отсутствие транзитивности, что достаточно плохо. Рассмотреть возможность:Что это значит?
p1
есть руководитель, которого зовут "Алиса".p2
имеет руководителя, которого зовут «Нет».p3
явно, однозначно, не имеет руководителя .p4
может или может иметь руководителя. Мы не знаем, нам все равно, мы не должны знать (проблема конфиденциальности?), Поскольку это не наше дело.Когда вы используете,
==
вы смешиваете,null
иundefined
это совершенно неправильно. Два термина означают совершенно разные вещи! Сказать, что у меня нет руководителя, просто потому, что я отказался сказать вам, кто мой руководитель, и это неправильно!Я понимаю, что есть программисты, которые не заботятся об этой разнице
null
иundefined
или предпочитают использовать эти термины по-другому. И если ваш мир не используетnull
иundefined
правильно, или вы хотите дать свое собственное толкование этих терминов, пусть будет так. Я не думаю, что это хорошая идея.Теперь я , кстати , не имеют никаких проблем с
null
иundefined
оба они falsy! Это совершенно нормально сказатьа затем
null
иundefined
приведет к пропуску кода, который обрабатывает супервизор. Это правильно, потому что мы не знаем или не имеем руководителя. Все хорошо. Но две ситуации не равны . Вот почему==
это неправильно. Опять же, вещи могут быть ложными и использоваться в смысле утки, что отлично подходит для динамических языков. Это правильный JavaScript, Pythonic, Rubyish и т. Д. Но опять же, эти вещи НЕ равны.И не заставляйте меня начинать с нетранзитивности
"0x16" == 10
,10 == "10"
но нет"10" == "0x16"
. Да, JavaScript слабо набирает. Да, это принудительно. Но принуждение никогда не должно относиться к равенству.Кстати, у Крокфорда действительно сильные мнения. Но вы знаете, что? Он здесь прав!
Я понимаю, что есть ситуации, в которых мне
==
удобно! Как взятие строкового ввода для чисел и, скажем, сравнение с 0. Однако, это взлом. У вас есть удобство как компромисс для неточной модели мира.TL; DR: ложь - отличная концепция. Это не должно распространяться на равенство.
источник
p5
... единственную ситуацию,typeof(p5.supervisor) === typeof(undefined)
когда супервизор даже не существует в качестве концепции: D