В чем разница между (NaN!= NaN) и (NaN!= NaN)?

Прежде всего, хочу отметить, что я знаю, как работают isNaN() и Number.isNaN(). Я читаю "Определенное руководство" Дэвида Фланагана, и он приводит пример того, как проверить, имеет ли значение NaN:

x !== x

Это приведет к true тогда и только тогда, когда x будет NaN.

Но теперь у меня есть вопрос: почему он использует строгое сравнение? Потому что кажется, что

x != x

ведет себя одинаково. Безопасно ли использовать обе версии или я пропускаю некоторые значения в JavaScript, которые возвратят true для x !== x и false для x != x?

Ответ 1

Во-первых, позвольте мне заметить, что NaN - очень специальное значение: по определению оно не равно самому себе. Это исходит из стандарта IEEE-754, на который нарисованы номера JavaScript. Значение "не число" никогда не совпадает с самим собой, даже если бит является точным совпадением. (Что они не обязательно в IEEE-754, это позволяет использовать несколько разных значений "не число".) Вот почему это даже появляется; все остальные значения в JavaScript равны самим себе, NaN является только специальным.

... Я пропустил какое-то значение в JavaScript, которое вернет true для x! == x и false для x!= x?

Нет, это не так. Единственное различие между !== и != заключается в том, что последний будет прибегать к типу принуждения, если необходимо, чтобы типы операндов были одинаковыми. В x != x типы операндов одинаковы, поэтому они точно такие же, как x !== x.

Это ясно из начала определения Abstract Equality Operation:

  • ReturnIfAbrupt (х).
  • ReturnIfAbrupt (у).
  • Если тип (x) совпадает с типом (y), то

    Возвращает результат выполнения строгого сравнения равенств x === y.

  • ...

Первые два шага - это базовая сантехника. Таким образом, самый первый шаг == заключается в том, чтобы увидеть, являются ли типы одинаковыми и, если да, сделать ===. != и !== являются просто отрицательными версиями этого.

Итак, если Flanagan верен, что только NaN выдаст true для x !== x, мы можем быть уверены, что оно также верно, что только NaN выдаст true для x != x.

Многие программисты JavaScript по умолчанию используют === и !==, чтобы избежать некоторых ловушек вокруг тикового принуждения, которые выполняют свободные операторы, но в этом случае нет ничего, что можно было бы использовать в использовании Flanagan для строгого и свободного оператора.

Ответ 2

Для целей NaN, != и !== делают то же самое.

Однако многие программисты избегают == или != в JavaScript. Например, Дуглас Крокфорд считает их среди "плохих частей" языка JavaScript, потому что они ведут себя непредсказуемыми и запутанными способами:

В JavaScript есть два набора операторов равенства: === и !==, а их злые близнецы == и !=. Хорошие работают так, как вы ожидали.

... Мой совет - никогда не использовать злых близнецов. Вместо этого всегда используйте === и !==.

Ответ 3

Просто для удовольствия, позвольте мне показать вам искусственный пример, где x не NaN, но в любом случае операторы ведут себя по-другому. Сначала определите:

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

Тогда имеем

x != x // false

но

x !== x // true

Ответ 4

Я просто хочу указать, что NaN - это не единственная вещь, которая производит x !== x без использования глобального объекта. Существует множество умных способов вызвать это поведение. Здесь используется метод getters:

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

Как отмечают другие ответы, == выполняет операции с типом, но как в других языках, так и в стандарте - NaN указывает на сбой вычисления и по уважительным причинам не равен самому себе.

По какой-то причине люди не рассматривают эту проблему с JS, но большинство языков, которые имеют удвоение (а именно C, Java, С++, С#, Python и др.), демонстрируют это точное поведение, и люди в порядке с ним.

Ответ 5

Как иногда изображения лучше, чем слова, проверьте table (вот почему я должен сделать этот ответ вместо комментарий потому, что он становится лучше видимым).

Там вы можете видеть, что строгое сравнение равенства (===) возвращает true только в том случае, если тип и контент совпадают, поэтому

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

В то время как абстрактное сравнение равенства (==) проверяет только содержимое * путем преобразования типов, а затем строго их сравнивает:

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

Хотя это неясно, не советуя ECMA, что JavaScript рассматривает при сравнении, таким образом, что приведенный ниже код оценивается как true.

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