Чому в JavaScript чому "0" дорівнює false, а коли тестується "if", воно не відповідає false?


232

Нижче показано, що "0"в JavaScript невірно:

>>> "0" == false
true

>>> false == "0"
true

То чому ж друкується наступне "ha"?

>>> if ("0") console.log("ha")
ha

47
"0"є рядком, і оскільки він не порожній, він оцінюється як істинний.
— Цифровий літак

8
"0" === false [...] false

3
Ознайомтеся з правдою статті статті Ангеса Кролла в JavaScript. javascriptweblog.wordpress.com/2011/02/07/…
— timrwood

8
'0'==falseале "0" не є фальсифікованим значенням (так, Javascript може бути дивним)
— Linsey

5
@Linsey: Уся річ "хибної" та "неправдоподібної" речі колись була призначена лише для пояснення того, як значення перетворюються на булі. Якщо порівнювати два значення з ==, вони ніколи не перетворюються на булеві, тому це не застосовується. (Правила перетворення, здається, сприяють перетворенню на числа.)
— Мілімооз

Відповіді:


251

Причина полягає в тому, що коли ви явно це робите "0" == false, обидві сторони перетворюються на числа, а потім проводиться порівняння.

Коли ви робите:, if ("0") console.log("ha")значення рядка тестується. Будь-яка не порожня рядок є true, а порожня - рядок false.

Дорівнює (==)

Якщо два операнди не одного типу , JavaScript перетворює операнди, то застосовується суворе порівняння. Якщо будь-який операнд є числом або булевим , операнди перетворюються на числа, якщо це можливо; інакше, якщо будь-який операнд є рядком , інший операнд перетворюється на рядок, якщо це можливо. Якщо обидва операнди є об'єктами , то JavaScript порівнює внутрішні посилання, які рівні, коли операнди посилаються на один і той же об'єкт у пам'яті.

(Від операторів порівняння в мережі розробників Mozilla)


348

Таблиці, що відображають проблему:

truthy якщо твердження

і == truthy порівняння всіх типів об’єктів у JavaScript

Мораль використання історії === сувора рівність, що демонструє розум

Кредит на створення таблиці: https://github.com/dorey/JavaScript-Equality-Table


2
Це має набагато більше сенсу з іншого порядку значень gist.github.com/kirilloid/8165660
— kirilloid

3
Відтепер, якщо хтось скаже, що він ніколи не використовує суворих операторів порівняння, я зіткнуся з ним за цими таблицями і змушу його плакати. Досі не впевнений, чи розумію я цю концепцію NaN. Я маю на увазі, typeof NaN // numberале NaN === NaN // false, гм ...
— Юстус Ромінь

4
Мій друг зробив f.cl.ly/items/3b0q1n0o1m142P1P340P/javascript_equality.html - ті ж графіки, що і вище, але трохи легше читати.
— Люсі Бейн

@JustusRomijn є декілька значень для представлення NaN, тому, коли ви порівнюєте 2 NaN, вони мають різні значення (я думаю). Прочитайте першу цитату тут .
— cychoi

4
Ці таблиці мають помилку. Ні , ==ні ===оператор для [], {}, [[]], [0]і [1]значення не обчислюватися так. Я маю на увазі [] == []і [] === []також помилково.
— Гербертуш

38

Це згідно спец.

12.5 Заява if 
.....

2. Якщо вірно ToBoolean (GetValue (exprRef)), то 
а. Поверніть результат оцінки першої заяви.
3. Ще, 
….

Відповідно до специфікації, ToBoolean є

Абстрактна операція ToBoolean перетворює свій аргумент у значення типу Boolean згідно таблиці 11:

І ця таблиця говорить про рядки:

введіть тут опис зображення

Результат помилковий, якщо аргументом є порожній рядок (його довжина дорівнює нулю); інакше результат правдивий

Тепер, щоб пояснити, чому "0" == falseви повинні прочитати оператор рівності, який стверджує, що він отримує своє значення, якщо абстрактна операція GetValue(lref)відповідає однаковій для правої частини.

Що описує цю відповідну частину як:

якщо IsPropertyReference (V), то 
а. Якщо HasPrimitiveBase (V) помилковий, то нехай get є внутрішнім методом бази [[Get]], інакше нехай отримує
бути спеціальним внутрішнім методом [[Get]], визначеним нижче. 
б. Поверніть результат виклику внутрішнього методу get, використовуючи base як його це значення, і передаючи
GetReferencedName (V) для аргументу

Або іншими словами, рядок має примітивну базу, яка викликає внутрішній метод отримання і виявляється помилковою.

Якщо ви хочете оцінити речі за допомогою операції GetValue ==, якщо ви хочете оцінити за допомогою функції ToBoolean, використовуйте ===(також відомий як "суворий" оператор рівності)


"a string has a primitive base, which calls back the internal get method and ends up looking false"Це правда для всіх рядків?
— aziz punjani

@Interstellar_Coder Section 8.12.3: [[Get]] (P)описує, як це працює. Це справедливо лише для випадків, коли рядок дорівнює 0, оскільки це робить купу інших внутрішніх дзвінків, врешті-решт, в результаті GetOwnPropertyчого бачиться, що "все" - це властивість даних, яка потім повертає все це значення назад. Ось чому "0" помилково, а "бла" - істинно. Перегляньте кілька відео Дугласа Крокфорда про театр розробників Yahoo, він описує "truthyness" в JavaScript трохи менш складним, ніж я. Якщо ви зрозумієте, що означає "правда" та "хибність", ви зрозумієте відповідь Бобенса відразу.
— інкогніто

1
Де я можу знайти специфікацію?
— user985366

12

Це PHP, де рядок "0"є хибним (false-when-used-in-boolean-context). У JavaScript всі непусті рядки є правдоподібними.

Хитрість полягає в тому, що ==проти булевого значення не оцінюється в булевому контексті, він перетворюється на число, а у разі рядків, що робиться шляхом розбору як десятковий. Таким чином, ви отримуєте Номер 0замість булінів правдивості true.

Це дійсно поганий мовний дизайн, і це одна з причин, коли ми намагаємося не використовувати нещасного ==оператора. Використовуйте ===замість цього.


7
// I usually do this:

x = "0" ;

if (!!+x) console.log('I am true');
else      console.log('I am false');

// Essentially converting string to integer and then boolean.

4

Ваші цитати 0роблять це рядком, який оцінюється як істинний.

Видаліть цитати, і це повинно працювати.

if (0) console.log("ha") 

правильно, а не про те, як "змусити це працювати", але питання більше схоже на "чому він так поводився?"
— неополярність

2

Це все через специфікації ECMA ... "0" == falseчерез правила, вказані тут http://ecma262-5.com/ELS5_HTML.htm#Section_11.9.3 ... І if ('0')оцінює справжнє через правила, вказані тут http: / /ecma262-5.com/ELS5_HTML.htm#Section_12.5


Я не знав, що хтось переніс специфікацію на сайт ... це приголомшливо! Більше не для мене файлів PDF.
— інкогніто

1

Тести експресії "якщо" на істинність, у той час як подвійні рівні тести на незалежність від типу еквівалентності. Рядок - це завжди триут, як це вказували інші. Якби подвійне рівне тестувало обидва його операнди на правдивість, а потім порівнювало результати, то ви отримали б результат, який ви інтуїтивно припускали, тобто ("0" == true) === true. Як каже Дуг Крокфорд у своєму відмінному JavaScript: "Хороші частини" , "правила, за якими [== примушує типи його операндів] є складними і незабутніми .... Відсутність транзитивності насторожує". Досить сказати, що один з операндів примусований до типу, щоб відповідати іншому, і що "0" в кінцевому підсумку трактується як числовий нуль,


1

== Оператор рівності оцінює аргументи після перетворення їх у числа. Таким чином, рядок нульовий "0" перетворюється на тип даних номера, а булева помилка перетворюється на число 0. Отже

"0" == false // true

Те ж стосується `

false == "0" //true

=== Сувора перевірка рівності оцінює аргументи з оригінальним типом даних

"0" === false // false, because "0" is a string and false is boolean

Те саме стосується і

false === "0" // false

В

if("0") console.log("ha");

Рядок "0" не порівнює з жодними аргументами, а рядок - це справжнє значення до тих пір, поки воно не порівнюється з будь-якими аргументами. Це саме так

if(true) console.log("ha");

Але

if (0) console.log("ha"); // empty console line, because 0 is false

`


1

Це тому, що JavaScript використовує тип примусу в булевих контекстах та вашому коді

if ("0") 

буде примусово до істини в булевих контекстах.

У Javascript є й інші truthy значення, які будуть примусові до істинних у булевих контекстах, і таким чином виконати блок if:

if (true)
if ({})
if ([])
if (42)
if ("0")
if ("false")
if (new Date())
if (-42)
if (12n)
if (3.14)
if (-3.14)
if (Infinity)
if (-Infinity)

0
if (x) 

примушує x за допомогою внутрішнього JavaScript toBoolean (http://es5.github.com/#x9.2)

x == false

примушує обидві сторони, використовуючи внутрішній примус до Number (http://es5.github.com/#x9.3) або toPrimitive для об'єктів (http://es5.github.com/#x9.1)

Для отримання детальної інформації див. Http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/

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