Используется ли использование JavaScript в JavaScript?

В 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 с помощью оператора ===.

Учитывая это недвусмысленное наблюдение, существует ли когда-нибудь время, когда использование == может быть действительно подходящим?

271 голос | спросил Robert Harvey 6 Jam1000000amTue, 06 Jan 2015 01:29:23 +030015 2015, 01:29:23

7 ответов


223

Я собираюсь сделать аргумент для ==

Дуглас Крокфорд, который вы цитировали, известен своими многочисленными и часто очень полезными мнениями. Хотя я с Крокфордом в этом конкретном случае, стоит упомянуть, что это не мнение only . Есть и другие, такие как создатель языка Брендан Эйх, которые не видят большой проблемы с ==. Аргумент выглядит примерно так:

JavaScript - это поведенчески * типизированный язык. Вещи рассматриваются на основе того, что они могут делать, а не их фактического типа. Вот почему вы можете вызвать метод массива .map в NodeList или в наборе выбора jQuery. Также вы можете сделать 3 - "5" и получить что-то значимое назад, потому что «5» может действовать как число.

Когда вы выполняете равенство ==, вы сравниваете содержимое переменной, а не ее типа . Вот некоторые примеры, когда это полезно:

  • Чтение числа от пользователя - прочитайте .value входного элемента в DOM? Нет проблем! Вам не нужно начинать кастинг или беспокоиться о его типе - вы можете == сразу же перечислить цифры и получить что-то значимое.
  • Нужно проверить «существование» объявленной переменной? - вы можете == null, так как поведенческий null означает, что там ничего нет и undefined не имеет ничего там.
  • Необходимо проверить, есть ли у вас значимый вклад от пользователя? - проверьте, является ли вход ложным с аргументом ==, он будет обрабатывать случаи, когда пользователь ничего не ввел или просто белое пространство для вас, что, вероятно, вам нужно.

Давайте посмотрим на примеры Крокфорда и объясним их поведенчески:

'' == '0'           // got input from user vs. didn't get input - so false
0 == ''             // number representing empty and string representing empty - so true
0 == '0'            // these both behave as the number 0 when added to numbers - so true    
false == 'false'    // false vs got input from user which is truthy - so false
false == '0'        // both can substitute for 0 as numbers - so again true

false == undefined  // having nothing is not the same as having a false value - so false
false == null       // having empty is not the same as having a false value - so false
null == undefined   // both don't represent a value - so true

' \t\r\n ' == 0     // didn't get meaningful input from user vs falsey number - true 

В принципе, == предназначен для работы на основе того, как примитивы behave в JavaScript не основаны на том, что они . Хотя я лично не согласен с этой точкой зрения, определенно заслуживает этого - особенно если вы примете эту парадигму обработки типов, основанных на поведении на языке.

* некоторые могут предпочесть название структурной типизации, которое более распространено, но есть разница - не очень интересно обсуждать здесь разницу.

ответил Benjamin Gruenbaum 6 Jam1000000amTue, 06 Jan 2015 10:03:31 +030015 2015, 10:03:31
91

Оказывается, что jQuery использует конструкцию

if (someObj == null) {
  // do something
}

широко, как сокращенное обозначение эквивалентного кода:

if ((someObj === undefined) || (someObj === null))  {
  // do something
}

Это является следствием Спецификации языка ECMAScript § 11.9.3, Абстрактное равенство Алгоритм сравнения , в котором, среди прочего, говорится, что

1.  If Type(x) is the same as Type(y), then  
    a.  If Type(x) is Undefined, return true.  
    b.  If Type(x) is Null, return true.

и

2.  If x is null and y is undefined, return true.
3.  If x is undefined and y is null, return true.

Эта конкретная техника достаточно распространена, что JSHint имеет специально для этого.

ответил Robert Harvey 6 Jam1000000amTue, 06 Jan 2015 01:29:23 +030015 2015, 01:29:23
14

Проверка значений для 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". Вы можете сказать, что это ужасная идея, позволяющая реализовать сравнение строк и чисел путем разбора строки. Но учитывая, что это так, как это работает, == является абсолютно правильным способом реализации этого оператора отношений.

Итак, действительно хорошая вещь о == заключается в том, что она совместима со всеми другими отношениями. Иначе говоря, если вы пишете это:

function compare(a, b) {
  if (a > b) return 1;
  if (a < b) return -1;
  return 0;
}

Вы уже неявно используете ==.

Теперь к довольно связанному вопросу: было ли плохое решение реализовать сравнение чисел и строк так, как оно реализовано? Видно в изоляции, это выглядит довольно глупо. Но в контексте других частей JavaScript и DOM это относительно прагматично, учитывая, что:

  • атрибуты всегда строки
  • Клавиши
  • всегда являются строками (в том случае, если вы используете Object, чтобы иметь разреженную карту от ints до значений)
  • Пользовательский ввод и значения управления формой всегда являются строками (даже если исходный код соответствует input[type=number])

По целому ряду причин имеет смысл заставить строки вести себя как числа, когда это необходимо. Предполагая, что сравнение строк и конкатенация строк имеют разные операторы (например, :: для конкатессии и метод сравнения (где вы можете использовать всевозможные параметры для чувствительности к регистру, а какие нет)), это было бы факт быть менее беспорядочным. Но эта перегрузка оператора, вероятно, есть на самом деле, откуда «Java» в «JavaScript» приходит;)

ответил back2dos 6 Jpm1000000pmTue, 06 Jan 2015 18:37:32 +030015 2015, 18:37:32
7

Да, я использовал пробел для него, а именно, когда вы сравниваете ключ с числовым значением:

for (var key in obj) {
    var some_number = foo(key, obj[key]);  // or whatever -- this is just an example
    if (key == some_number) {
        blah();
    }
}

Я думаю, что более естественным является сравнение с key == some_number, а не как Number(key) === some_number или как key === String(some_number).

ответил Mehrdad 7 Jam1000000amWed, 07 Jan 2015 03:51:09 +030015 2015, 03:51:09
7

Как профессиональный математик, я вижу в оператор сходства Javscript == (также называется " абстрактное сравнение "," свободное равенство ") попытка построить отношение эквивалентности между сущности, которые включают рефлексивные , симметричный и переходный . К сожалению, два из этих трех основных свойств не выполняются:

== не рефлексивный :

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 is 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, внезапно не похожи! Почему или почему? Согласно правилу, строки точно равны, когда они строго равны! Не только это правило нарушает транзитивность, как мы видим, но и избыточно! Какой смысл создать другой оператор ==, чтобы он был строго идентичен другому ===?

Заключение

Свободный оператор равенства == мог бы быть очень полезным, если он соблюдает некоторые фундаментальные математические законы. Но, как это, к сожалению, нет, его польза страдает.

ответил Dmitri Zaitsev 16 Maypm16 2016, 19:16:40
3

Сегодня я встретил довольно полезное приложение. Если вы хотите сравнить заполненные цифры, например 01 с нормальными целыми числами, == работает просто отлично. Например:

'01' == 1 // true
'02' == 1 // false

Это избавляет вас от удаления 0 и преобразования в целое число.

ответил Jon Snow 8 Jpm1000000pmThu, 08 Jan 2015 12:27:10 +030015 2015, 12:27:10
3

Я знаю, что это поздний ответ, но, похоже, существует некоторая путаница в отношении null и undefined, что IMHO - это то, что делает == evil, тем более что недостаток транзитивности, который является достаточно плохим. Рассмотрим:

p1.supervisor = 'Alice';
p2.supervisor = 'None';
p3.supervisor = null;
p4.supervisor = undefined;

Что это значит?

  • p1 имеет супервизора, чье имя - «Алиса».
  • p2 имеет супервизора, чье имя «Нет».
  • p3 явно, недвусмысленно, не имеет диспетчера .
  • p4 может иметь или иметь супервизора. Мы не знаем, нам все равно, мы не должны знать (вопрос о конфиденциальности?), Поскольку в этом нет нашего бизнеса.

Когда вы используете ==, вы объединяете null и undefined, который полностью не подходит. Эти два термина означают совершенно разные вещи! Говоря, что у меня нет супервизора, просто потому, что я отказался сказать вам, кто мой руководитель - это неправильно!

Я понимаю, что есть программисты, которые не заботятся об этой разнице между null и undefined или предпочитают использовать эти термины по-разному. И если ваш мир не использует null и undefined правильно, или вы хотите дать свою интерпретацию этим условиям, пусть будет так. Я не думаю, что это хорошая идея.

Теперь, кстати, у меня нет проблем с null и undefined, которые являются ложными! Это нормально сказать

if (p.supervisor) { ... }

, а затем null и undefined приведет к пропуску кода, который обрабатывает супервизор. Это правильно, потому что мы не знаем или не имеем супервизора. Все хорошо. Но две ситуации не равны . Вот почему == неверен. Опять же, все может быть ложным и использоваться в смысле утиного ввода, что отлично подходит для динамических языков. Это правильный JavaScript, Pythonic, Rubyish и т. Д. Но опять же, эти вещи НЕ равны.

И не заставляйте меня начинать с не-транзитивности: "0x16" == 10, 10 == "10", но не "10" == "0x16". Да, JavaScript слабо типов. Да, это принудительно. Но принуждение никогда не должно применяться к равенству.

Кстати, у Крокфорда есть сильные мнения. Но вы знаете, что? Он здесь прав!

FWIW Я понимаю, что есть, и я лично сталкивался с ситуациями, где == удобен! Подобно принятию строкового ввода для чисел и, скажем, сравнению с 0. Однако это взломать. У вас есть удобство как компромисс для неточной модели мира.

TL; DR: ложность - отличное понятие. Он не должен распространяться на равенство.

ответил Ray Toal 11 Jpm1000000pmSun, 11 Jan 2015 23:19:41 +030015 2015, 23:19:41

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132