Яка різниця між (NaN! = NaN) і (NaN! == NaN)?


148

Перш за все хочу зазначити, що я знаю, як isNaN()і як Number.isNaN()працюю. Я читаю Дефінітний посібник Девіда Фланагана, і він наводить приклад того, як перевірити, чи є значення NaN:

x !== x

Це призведе до того, trueякщо і тільки якщо xє NaN.

Але зараз у мене питання: чому він використовує суворе порівняння? Бо здається, що так

x != x

поводиться так само. Чи безпечно використовувати обидві версії, або мені не вистачає якихось значень у JavaScript, які повертатимуться trueза x !== xі falseдля x != x?


10
Можливо, Фланаган просто віддає перевагу !==чекам над !=чеками. Наскільки я знаю, немає іншого значення де x != x. Але є дві окремі групи розробників JavaScript: ті, хто віддає перевагу, !=і ті, хто вважає за краще !==, будь то швидкість, ясність, виразність тощо
Стів Кльостерс

30
Навіщо використовувати слабке порівняння, коли суворе порівняння поводиться однаково?
Ри-

3
@Raulucco: NaNце не унікальний тип, це число. Це унікальне значення, яке не дорівнює собі.
TJ Crowder

8
Назва, здається, вводить людей в оману. Я б запропонував змінити його на щось на кшталт "Чи x! = X колись відрізняється від x! == x?"
TJ Crowder

6
@femmestem: Джорджі сказав: "у цьому випадку" це питання стилю. І він у цьому правильний. Це НЕ стиль , коли типи операндів різні, але це стиль , коли вони однакові. Окремо: Фланаган проводить ті порівняння ===з NaN, щоб зробити висновок, що NaN не дорівнює собі. Він не «помиляється», він робить це як навчальну вправу, демонструючи, що це не працює.
TJ Crowder

Відповіді:


128

По-перше, дозвольте зазначити, що NaNце дуже особливе значення: За визначенням, воно не рівне собі. Це походить від стандарту IEEE-754, на якому позначаються номери JavaScript. Значення "не число" ніколи не дорівнює собі, навіть коли біти точно збігаються. (Що вони необов’язково в IEEE-754, це дозволяє кілька різних значень "не число".) Тому це навіть з'являється; всі інші значення в JavaScript рівні собі, NaNпросто особливі.

... я пропускаю якесь значення в JavaScript, яке поверне справжнє для x! == x та false для x! = x?

Ні, не ти. Єдина відмінність між !==і !=полягає в тому, що останні будуть виконувати примус, якщо необхідно, щоб типи операндів були однаковими. У x != x, типи операндів однакові, і тому вони точно такі ж, як x !== x.

Це зрозуміло з початку визначення операції "Абстракційна рівність" :

  1. ReturnIfAbrupt (x).
  2. ReturnIfAbrupt (y).
  3. Якщо Type (x) такий же, як Type (y), то

    Поверніть результат виконання суворого порівняння рівності x === y.

  4. ...

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

Тож якщо Фланаган правильний, що відповідає лише NaNістині x !== x, ми можемо бути впевнені, що це також правда, що тільки NaNдасть істину для x != x.

Багато програмістів JavaScript за замовчуванням використовують ===та !==уникають деяких підводних каменів навколо примусу типу, який виконують вільні оператори, але в цьому випадку використання Фланагана оператора суворого та вільного в цьому випадку нічого не можна прочитати.


Я перечитав 4.9.1 - Equality and Inequality Operatorsрозділ, і це, здається, є відповіддю. Ключовий момент для ===порівняння: If the two values have the same type, test them for strict equality as described above. If they are strictly equal, they are equal. If they are not strictly equal, they are not equal.
Джорджі Накері

@GiorgiNakeuri: Я не впевнений, про який 4.9.1 ви маєте на увазі, можливо, книгу Фланагана? Але це в основному говорить про те, що говорить цитата з специфікації вище, так.
TJ Crowder

2
Я приймаю це, тому що це відповідає на моє запитання формально та точно. Дякую за пояснення!
Джорджі Накері

1
@Moshe: Що ви маєте на увазі під «живими прив’язками»? (Термін не відображається в специфікації.) Ви маєте на увазі щось на зразок прикладу GOTO 0, де aнасправді є функцією і не повертає одне і те ж значення двічі? Це не те саме, що значення, яке !==було б істинним, про що запитувала ОП. Це просто функція, яка повертає різні значення. foo() !== foo()Не обов'язково істинно, оскільки fooможе повертати різні значення під час кожного дзвінка.
TJ Crowder

1
@Moshe Ну, це надзвичайно неприємний спосіб возитися з властивостями та людьми. Але це, схоже, майже те саме, що є прикладом GOTO 0, лише з додатковим шаром опосередкованості.
JAB

37

Для цілей NaN, !=і !==робити те ж саме.

Тим НЕ менше, багато програмістів уникнути ==або !=в JavaScript. Наприклад, Дуглас Крокфорд вважає їх одними з " поганих частин " мови JavaScript, оскільки вони поводяться несподівано і заплутано:

У JavaScript є два набори операторів рівності: ===і !==, і їхні злі близнюки ==та !=. Хороші працюють так, як ви очікували.

... Моя порада ніколи не використовувати злих близнюків. Натомість завжди використовуйте ===і !==.


2
Питання не в NaN (незважаючи на назву). Питання полягає в тому, "чи я пропускаю якесь значення в JavaScript, яке поверне значення true для x! == x та false для x! = X?"
TJ Crowder

@TJCrowder Дійсно два питання. Перше питання "Чи безпечно використовувати обидві версії", а відповідь - обидві версії рівноцінні. Мені подобається ваша відповідь "під кришкою", яка детально пояснює все.
jkdev

22

Для розваги дозвольте показати вам штучний приклад, де xце не так, NaNале оператори все одно поводяться по-різному. Спочатку визначте:

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

Тоді маємо

x != x // false

але

x !== x // true

9
Га! :-) Але це ефективно, foo() != foo()коли foo повертає 1, то 2. Напр., Значення не однакові, це лише порівняння різних значень.
TJ Crowder

2

Я просто хочу зазначити, що NaNце не єдине, що виробляє x !== xбез використання глобального об'єкта. Існує маса розумних способів викликати таку поведінку. Ось один із використання геттерів:

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, C ++, C #, Python та інші), проявляють таку точну поведінку, і люди просто добре ставляться до цього.


2
Так, саме це @TJCrowder згадувало в коментарі до відповіді GOTO_0, чи не так?
Джорджі Накері

Не могли б ви пояснити, як отримати двозначний примус у цих інших мовах?
chicocvenancio

0

Як іноді, зображення кращі за слова, перевірте цю таблицю (саме тому я ставлю на це відповідь, а не коментар - це покращує видимість).

Там ви бачите, що суворе порівняння рівності (===) повертає істину лише у тому випадку, якщо тип і вміст збігаються, так

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

Хоча абстрактне порівняння рівності (==) перевіряє лише вміст * шляхом перетворення типів, а потім суворо порівнюючи їх:

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

Хоча незрозуміло, без консультацій з ECMA , що JavaScript враховує при порівнянні, таким чином, що код нижче оцінюється як істинний.

 var howAmISupposedToKnowThat = [] == false; //true
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.