Почему `null> = 0 && null <= 0`, но не` null == 0`?

144

Мне пришлось написать процедуру, которая увеличивает значение переменной на 1, если ее тип равен, numberи присваивает переменной 0, если нет, где переменная изначально nullили undefined.

Первая реализация была v >= 0 ? v += 1 : v = 0связана с тем, что я думал, что что-то, кроме числа, сделает арифметическое выражение ложным, но это было неверно, поскольку null >= 0оно оценивается как истинное. Затем я узнал, что nullведет себя как 0, и все следующие выражения оцениваются как истинные.

  • null >= 0 && null <= 0
  • !(null < 0 || null > 0)
  • null + 1 === 1
  • 1 / null === Infinity
  • Math.pow(42, null) === 1

Конечно, nullне 0. null == 0оценивается как ложь. Это делает кажущееся тавтологическим выражение (v >= 0 && v <= 0) === (v == 0)ложным.

Почему nullпохоже на 0, хотя на самом деле это не 0?

К. Ли
источник
3
Он говорит о Javascript. Ваш пример находится на PHP. В PHP оператор == сравнивает значения особым образом. Вы можете провести действительно сумасшедшие сравнения, такие как «10» == «1e1» (что верно). Если бы вы использовали оператор ===, вы бы получили совершенно другой результат, потому что он проверяет соответствие типа и значения. Проверьте эту ссылку: php.net/manual/en/language.operators.comparison.php
Pijusn
Оператор PHP '==' действительно работает "особым" образом.
Two-Bit Alchemist
Если ваше требование состояло в том, чтобы начать отсчет с 1 вместо 0, есть действительно краткий способ увеличить счетчики, которые изначально либо nullлибо undefined:c = -~c // Results in 1 for null/undefined; increments if already a number
Атес Горал,
1
undefined- значение переменной для переменных, которые не были инициализированы. nullс другой стороны, это пустое значение объекта, и его не следует смешивать с числами. nullне следует сочетать с числами, поэтому null не должен вести себя как числа.
Мэтью
1
@AtesGoral - кратко, но неочевидно. Стоит напомнить людям, что всякий раз, когда вы делаете что-то неочевидное, пожалуйста, добавьте комментарий, объясняющий, что делает код. В большинстве ситуаций я бы посчитал это «преждевременной оптимизацией», учитывая, что она торгует ясностью ради минимального прироста производительности.
ToolmakerSteve

Ответы:

209

Кажется, ваш настоящий вопрос:

Зачем:

null >= 0; // true

Но:

null == 0; // false

Что действительно происходит, так это то, что оператор « Больше или равно» ( >=) выполняет приведение типа ( ToPrimitive) с типом подсказкиNumber , фактически все операторы отношения имеют такое поведение.

nullособым образом обрабатывается оператором равенства ( ==). В кратком, это только принуждает к undefined:

null == null; // true
null == undefined; // true

Значение таких как false, '', '0', и []подлежат числовой тип принуждения, все они принуждают к нулю.

Вы можете увидеть внутренние детали этого процесса в алгоритме сравнения абстрактного равенства и алгоритме абстрактного реляционного сравнения .

В итоге:

  • Реляционное сравнение: если оба значения не относятся к типу String, ToNumberвызывается для обоих. Это то же самое, что добавление +впереди, которое для null приводит к 0.

  • Сравнение равенства: вызывает только ToNumberстроки, числа и логические значения.

Кристиан К. Сальвадо
источник
1
Привет, CMS, согласно вашему объяснению нулевой примитив равен 0, поэтому 0> = 0 возвращает true, а == возвращает false. Но согласно алгоритму ecma Если Type (x) - Object, а Type (y) - String или Number, вернуть результат сравнения ToPrimitive (x) == y. тогда в этом он должен вернуть истину. пожалуйста, объясните мне
bharath muppa 07
мне ответ не дает ответа - null is treated in a special way by the Equals Operator (==). In a brief, it only coerces to undefined:- и что? Вы можете объяснить, почему null >= 0? :)
Андрей Дейнеко
@bharathmuppa @ andrey-deineko: Остальная часть ответа CMS находится здесь: абстрактный алгоритм реляционного сравнения, который объясняет в пункте 3., что если оба значения не являются типом String, ToNumber вызывается на обоих. Это то же самое, что добавление +впереди, которое для null приводит к 0. Equality вызывает ToNumber только для строк, чисел и логических значений.
Майкл Ликури,
7
Хорошее описание, но оно мне не нравится. На любом языке (x == 0 || x> 0) должно быть эквивалентно (x> = 0). javascript - глупый язык.
Джон Хенкель
1
На самом деле это просто ошибка в спецификации (потому что математически это неверно), и с этим ничего не поделать, поскольку миллионы веб-сайтов полагаются на нулевые сравнения ^^ '
Махиддин
14

Я хотел бы расширить вопрос, чтобы еще больше улучшить видимость проблемы:

null >= 0; //true
null <= 0; //true
null == 0; //false
null > 0;  //false
null < 0;  //false

Это просто бессмысленно. Эти вещи, как и человеческие языки, нужно выучивать наизусть.

Estani
источник
1
Как описано выше, это можно объяснить за исключением того, как == обрабатывает null, иначе во всех случаях null преобразуется в 0 с помощью Number (nulll)
Сураб Ранка
5

В JavaScript есть как строгие сравнения, так и сравнения с преобразованием типов.

null >= 0;верно, но (null==0)||(null>0)неверно

null <= 0;верно, но (null==0)||(null<0)неверно

"" >= 0 также верно

Для реляционных абстрактных сравнений (<=,> =) операнды сначала преобразуются в примитивы, а затем в тот же тип перед сравнением.

typeof null returns "object"

Когда тип - объект, javascript пытается преобразовать объект в строку (т.е. null), предпринимаются следующие шаги ( ECMAScript 2015 ):

  1. Если PreferredTypeне прошел, пусть hintбудет «по умолчанию».
  2. Иначе , если PreferredTypeэто hintстрока, пусть hintбудет «строка».
  3. Else PreferredType- это hintномер, пусть hintбудет «номер».
  4. Пусть exoticToPrimбудет GetMethod(input, @@toPrimitive).
  5. ReturnIfAbrupt(exoticToPrim).
  6. Если exoticToPrimне определено, то
    а) Пусть будет результат Call(exoticToPrim, input, «hint»).
    б) ReturnIfAbrupt(result).
    c) Если Type(result)не Object, вернуть результат.
    г) Вызвать исключение TypeError.
  7. Если hint"по умолчанию", пусть hintбудет "номер".
  8. Вернуться OrdinaryToPrimitive(input,hint).

Допустимые значения подсказки: «по умолчанию», «число» и «строка». Объекты Date уникальны среди встроенных объектов ECMAScript в том смысле, что они рассматривают "default" как эквивалент "string". Все другие встроенные объекты ECMAScript рассматривают «по умолчанию» как эквивалент «числа» . ( ECMAScript 20.3.4.45 )

Думаю, nullконвертирует в 0.

Panos Kal.
источник
1

У меня такая же проблема !!. В настоящее время мое единственное решение - отделиться.

var a = null;
var b = undefined;

if (a===0||a>0){ } //return false  !work!
if (b===0||b>0){ } //return false  !work!

//but 
if (a>=0){ } //return true !
Джон
источник
Это может быть яснее вместо делать: if (a!=null && a>=0). Это проясняет причину, по которой нельзя просто >=действовать сам по себе: «a может иметь значение null (или undefined, что также равно '== null')».
ToolmakerSteve
0
console.log( null > 0 );  // (1) false
console.log( null == 0 ); // (2) false
console.log( null >= 0 ); // (3) true

Математически это странно. Последний результат гласит, что «null больше или равно нулю», поэтому в одном из приведенных выше сравнений он должен быть истинным, но оба они ложны.

Причина в том, что проверка на равенство ==и сравнения > < >= <=работают по-разному. Сравнения преобразуют null в число, обрабатывая его как 0. Вот почему (3) null >= 0есть, trueа (1) null > 0есть false.

С другой стороны, проверка равенства ==для undefinedи nullопределяется таким образом, что без каких-либо преобразований они равны друг другу и не равны ничему другому. Вот почему (2) null == 0есть false.

С. Хесам
источник