Javascript: Интересный случай Null >= 0

Составляя заметки для ускоренного курса по JavaScript, который я должен был преподать нескольким своим коллегам, я столкнулся с довольно интересным сценарием с null и операторами сравнения.

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

Подождите… подождите… чего? Это бессмыслица какая то!

как значение не может быть больше, чем 0, не может быть равно 0, но при этом быть больше или равно 0

Alt Text

Хотя я сначала это убрал, так как Javascript был Javascript, со всеми его причудливостями, но меня это заинтриговало. Имело ли это какое-то отношение к типу null и тому, как он обрабатывается или с тем как выполняются проверки сравнения отношений?

Поэтому я попытался найти основную причину этого и начал анализировать через единственный источник истины для Javascript — Javascript Spec.


Алгоритм абстрактного реляционного сравнения

Давайте сделаем первую проверку.

null > 0; // false

Согласно спецификации, операторы сравнения > и < отправляют утверждение через алгоритм, называемый алгоритмом абстрактного реляционного сравнения, чтобы выяснить, является ли утверждение true или false.

1. Вызов ToPrimitive (x, номер подсказки).

2. Вызов ToPrimitive (y, номер подсказки).

3. Если тип (результат (1)) является String и тип (результат (2)) является String, перейдите к шагу 16. (Обратите внимание, что этот шаг отличается от шага 7 в алгоритме для оператора сложения + при использовании 'и' вместо 'или'.)

4. Вызов ToNumber (Результат (1)).

5. Вызов ToNumber (Результат (2)).

6. Если результатом (4) является NaN, вернет значение undefined.

7. Если результатом (5) является NaN, вернет значение undefined.

8. Если результат (4) и результат (5) совпадают с числовым значение, вернет значение false.

9. Если результат (4) равен +0, а результат (5) равен -0, вернет значение false.

10. Если результат (4) равен -0, а результат (5) равен +0, вернет значение false.

11. Если результат (4) + ∞, вернет false.

12. Если результат (5) + ∞, вернет true.

13. Если результат (5) -∞, вернет false.

14. Если результат (4) -∞, вернет true.

15. Если математическое значение результата (4) меньше математического значения результата (5) ---- обратите внимание, что эти математические значения являются как конечными, так и не равными нулю - возвратит true. В противном случае вернет false.

16. Если результат (2) является префиксом результата (1), вернет false. (String значение p является префиксом строкового значения q, если q может быть результатом объединения p и некоторой другой строки r. Обратите внимание, что любая строка является префиксом самой, поскольку r может быть пустой строкой.)

17. Если результат (1) является префиксом результата (2), вернет true.

18. Пусть k - наименьшее неотрицательное целое число, такое, что символ в позиции k в результате (1) отличается от символа в позиции k в результате (2). (Там должен быть такой ak, поскольку ни одна строка не является префиксом другого.)

19. Пусть m - целое число, которое является значением кодовой точки для символа в позиции k в результата (1).

20. Пусть n - целое число, которое является значением кодовой точки для символа в позиции k в результата (2).

21. Если m < n, вернет true. В противном случае вернет false.

Давайте рассмотрим этот алгоритм с нашим утверждением — null > 0.

Шаги 1 и 2 просят нас вызвать ToPrimitive() с null и 0 соответственно, чтобы преобразовать эти значения в свои примитивные типы значений (например, Numberи String). Преобразования ToPrimitive следует из этой таблице.


Тип ввода результат
Undefined Нет конверсии
Null Нет конверсии
Boolean Нет конверсии
Number Нет конверсии
String Нет конверсии
Object Вернет значение по умолчанию для объекта. Значение по умолчанию для объекта извлекается, вызывая внутренний метод [[DefaultValue]] объекта, передавая необязательную подсказку PreferredType
,,,

Следуя таблице, наши null и 0, не подвергаются никакому преобразованию.

Так, Шаг 3, не относится к нам, поэтому мы можем игнорировать его и двигаемся дальше. На шагах 4 и 5 нам нужно преобразовать правую и левую часть в тип Number. Преобразование в Number в соответствии с этой таблицей.


Тип ввода результат
Undefined NaN
Null +0
Number Нет конверсии
Boolean Результат равен 1, если аргумент истинен. Результат равен +0, если аргумент ложный.

(Я пропустил String и Object из таблицы, поскольку они имеют более сложное преобразование, и не имеют отношения к нам в любом случае. Если вам это интересно, вы можете найти их здесь.)

null преобразуется в +0 a 0 остается 0. Ни одно из значений NaN так что позволяют нам пропустить шаги 6 и 7. На шаге 8 мы должны остановиться. +0 является равным 0, и алгоритм возвращает false. Следовательно,

null > 0; // false
а также 
null < 0; // также false


Алгоритм сравнения абстрактного равенства

Давайте рассмотрим следующую проверку.

null == 0; //  false

Это довольно интересно.

Оператор == запускает утверждение через Аннотация Равенства алгоритм сравнения, и возвращает true или false.

1. Если тип ( x ) отличается от типа ( y ), перейдите к шагу 14.

2. Если тип ( x ) не определен, вернет true.

3.Если тип ( x ) имеет значение Null, вернет true.

4. Если тип ( x ) не является числом, перейдите к шагу 11.

5. Если ( x ) является NaN, вернет false .

6. Если ( y ) - NaN, вернет false .

7. Если ( x ) - это то же числовое значение, что и ( y ) , вернет true .

8. Если ( x ) равно +0, а ( y ) равно -0 , вернет true .

9. Если ( x ) равно -0 и ( y ) равно +0 , вернет true .

10. Вернет значение false.

11.Если тип ( x ) является String, тогда вернет true, если ( x ) и ( y ) - это точно такая же последовательность символов (одинаковая длина и одинаковые символы в соответствующих позициях). В противном случае вернет false.

12. Если тип ( x ) является Boolean, вернет true, если ( x ) и ( y ) оба true или оба false. В противном случае вернет false .

13. Вернет true, если ( x ) и ( y ) относятся к одному и тому же объекту или относятся к объектам, соединенным друг с другом (см. 13.1.2 ). В противном случае вернет false.

14. Если ( x ) равно null, а y не определено, вернет true .

15. Если ( x ) не определено и y равно null, вернет true .

16.Если тип ( x ) является Number и тип ( y ) является String, вернет результат сравнения ( x ) == ToNumber ( y ).

17.Если тип ( x ) является String и тип ( y ) является Number, вернет результат сравнения ToNumber ( x) == ( y ) .

18. Если тип ( x ) является Boolean, вернет результат сравнения ToNumber ( x) == ( y ).

19. Если тип ( y ) булев, вернет результат сравнения x == ToNumber ( y ).

20.Если Type ( x ) является либо String, либо Number и тип ( y ) является Object, вернет результат сравнения ( x ) == ToPrimitive ( y ).

21.Если тип ( x ) является Object и тип ( y ) является либо String, либо Number, вернет результат сравнения ToPrimitive ( x) == ( y ) .

22. Вернет значение false.

Оценивая равенство null и 0, мы сразу переходим от шага 1 к шагу 14, поскольку типы не совпадают. Удивительно, но шаги 14 до 21, также не относятся к нам, так как тип (х) есть null. Наконец, мы достигнем шага 22, и значение false возвращается по умолчанию !
Следовательно,

null == 0; //  false


Оператор больше либо равно (>=)

И теперь мы добираемся до последней проверки.

null >= 0; // true

И вот тут спека полностью поразила меня. На очень высоком уровне реляционный оператор >= оценивает так

если null < 0 является false , тогда null >= 0 является true

Alt Text

Таким образом

null >= 0; // true

И это, если честно, имеет смысл. Математически, если у нас есть два числа, x и y, если x не меньше y, то x больше либо равно y .

Я предполагаю, что это сделано, чтобы оптимизировать оценку утверждения. Пожалуй если проверить что x больше y, и если это не так, если x равно y, тогда вы можете выполнить только одну оценку —  это x меньше чем y, а затем использовать этот результат для оценки.

(Если вас интересуют фактические шаги, выполняемые для оператора >=, вы можете найти их здесь .)

Какая банальная была эта проблема, поиск ответа привел на некоторые интересные выводы об языке. Надеюсь, эта статья сделает то же самое для вас.

© Habrahabr.ru