Null vs. undefined и их поведение в JavaScript
Итак, после большого аргумента/обсуждения/обсуждения реализации null и undefined в javascript мне хотелось бы, чтобы кто-то объяснял причины внедрения и почему они отличаются в некоторых обстоятельствах. Некоторые конкретные моменты, которые я нахожу тревожными:
-
null == undefined
оценивается как true
-
null + 1
равно 1, но undefined + 1
равно NaN
-
if(!null)
оценивается как true, а if(null)
- false, а null == false
- false.
Я прочитал спецификацию, и я знаю, как достигнуты результаты, я ищу парадигмы и причины, которые диктуют это спецификацией. Некоторые из этих пунктов, особенно второй, учитывая первое, чувствуют себя очень несовместимыми.
Ответы
Ответ 1
Коротка и сладкая версия заключается в том, что JavaScript был разработан и очень быстро реализован командой Netscape, и в нем были некоторые несоответствия, такие как те, которые вы указали.
Команда Internet Exploder сделала все возможное, чтобы точно скопировать JS, и они проделали чертовски хорошую работу, до такой степени, что несогласования были скопированы. Когда Netscape пошел, чтобы получить JS, стандартизованный как ECMAScript MS, был его частью и в основном сказал, что им не разрешалось изменять стандарт, потому что он нарушил бы старый код (существующая инерция систем). Несоответствия были стандартизированы, и это было так.
Douglas Crockford очень хорошая серия разговоров о некоторых из этих проблем.
Ответ 2
В первую очередь, в то время как множество языков уходит, не имея двух методов для подобной цели, в Javascript они выполняют различные, хотя и частично перекрывающиеся цели. "Почему оба?" был задан здесь раньше, и я нахожу, что этот ответ объясняет это довольно хорошо. TL; DR: Javascript имеет определенные языковые функции, которые генерируют отсутствующие значения в отличие от неинициализированных значений:
-
delete
'd values
- несуществующие свойства в объекте
- отсутствующие параметры функции
Что касается кажущихся противоречий в вашем вопросе, они на самом деле довольно легко объясняются спецификацией. (Я считаю, что даже можно утверждать, что объяснение изящно, хотя, вероятно, есть те, кто будет категорически не согласен.)
Адресация каждого из них отдельно:
- null == undefined оценивается как true
См. этот ответ для лучшего объяснения этого. Короче говоря, абстрактное сопоставление равенства указывает, что они (не строго) равны.
- null + 1 равно 1, но undefined + 1 равно NaN
Оператор +
функционирует как оператор unary + (числовое преобразование) или добавление, но оба направляют аргументы в параметр ToNumber, в котором говорится:
Тип аргумента — Результат
undefined — NaN
Null; +0
Boolean — Результат равен 1, если аргумент истинен. Результат равен +0, если аргумент false.
Номер; Результат равен входному аргументу (без преобразования).
Другими словами null + 1
становится +0 + 1
, а undefined + 1
становится NaN + 1
, который всегда NaN
.
- if (! null) оценивает значение true и если (null) вычисляется как false, но null == false вычисляется как false.
Как вы знаете, !
является оператором Logical Not, и он выполняет ToBoolean преобразование в выражение. Это тип усечения.
if
statement (if (expr)
) выполняет неявное логическое сравнение expr. Поэтому взгляните на тип выражения в обоих вышеперечисленных выражениях if
:
-
if (!null)
: expr is !null
, который, учитывая результат логического оператора not (!
), является булевым.
-
if (null)
: expr is null
, что означает, что никакое преобразование не выполнялось.
Поскольку оператор логический не выполняет фактическое преобразование, то же самое происходит и в других случаях, и на самом деле это не логическое противоречие, как кажется:
-
if (!"" == !undefined)
= true
-
if ("" == undefined)
= false, конечно.
Ответ 3
Их лучше всего рассматривать как совершенно разные объекты, используемые для разных целей:
null
используется для "не имеет значения". Он довольно редко используется языком, но часто используется средой-хозяином для обозначения "нет значения". Например, document.getElementById
возвращает null
для неэксентирующих элементов. Точно так же свойство IE-only onreadystatechange
для HTMLScriptElement
установлено null
, а не undefined
, чтобы показать, что, хотя свойство существует, оно в настоящее время не установлено. Обычно рекомендуется использовать null
в вашем собственном коде вместо undefined
и оставить undefined
для следующих случаев:
undefined
используется для "даже не установлен или даже не существует". Во многих случаях это "дефолт", например. доступ к свойству undefined (например, onreadystatechange
для HTMLScriptElement
в браузерах, отличных от IE), возвращаемое по умолчанию значение из методов без операторов return
, значение по умолчанию параметров функции при вызове функции с меньшим количеством аргументов, чем это объявляет и т.п.
Таким образом полезно придумать null
как "действительное значение", только одно означает что-то особенное. В то время как undefined
больше относится к языковому уровню.
Конечно, есть некоторые случаи, когда эти рассуждения не полностью сохраняются; это в основном по наследственным причинам. Но есть разница, и это одно, что имеет смысл.
Что касается ваших болевых точек, в частности, они в основном возникают из-за зла оператора ==
или типа принуждения:
-
null == undefined
: не используйте оператор ==
, потому что он по существу является беспорядком правил обратной совместимости, которые казались интуитивными в то время.
-
null + 1 === 1
vs. undefined + 1 === NaN
: оператор +
выполняет операцию принуждения до Number
перед оценкой. И null
приводит к 0
(+null === 0
), тогда как undefined
приводит к NaN
(isNaN(+undefined) === true
).
-
if (!null)
, if (null)
, null == false
: если оценивает "правду" или "ложность" своего аргумента, что не имеет никакого отношения к беспорядку правил для ==
. null
является ложным, а !null
является правдивым, но правила для ==
не позволяют null == false
.
Ответ 4
null == undefined
действительно оценивает значение true, но null === undefined
принимает значение false.
Разница в этих двух утверждениях - оператор равенства. Double-equal в Javascript преобразует два элемента в один и тот же тип перед их сопоставлением; для null == undefined
это означает, что null
преобразуется в переменную undefined до того, как выполняется сравнение, следовательно, это равенство.
Мы можем продемонстрировать тот же эффект со строками и целыми числами: "12" == 12
- true, но "12" === 12
- false.
Этот пример дает нам более простой способ обсудить ваш следующий момент, добавив один к каждому из них. В приведенном выше примере добавление 1 к целому выражению явно дает 13
, но с строкой "12" + 1
дается строка "121"
. Это имеет смысл, и вы не хотели бы этого по-другому, но с двукратным оператором исходные два значения были указаны как равные.
Урок здесь состоит в том, чтобы всегда использовать оператор тройного равенства, предпочитая двойное равенство, если у вас нет конкретной необходимости сравнивать переменные разных типов.
Ваша последняя точка демонстрирует непостоянный характер null
в целом. Это своеобразный зверь, так как любой, кто когда-либо пытался работать с нулевым полем базы данных, скажет вам. У Null есть очень специфическое определение в области информатики, которое реализовано аналогичным образом на нескольких языках, поэтому описанная вами ситуация не является особенной странностью Javascript. Нуль странно. Не ожидайте, что он будет вести себя как альтернативное имя для false
, потому что это не работает. Встроенное значение infinity
может вести себя так же странно и по аналогичным причинам.
Javascript действительно имеет свою долю странности. Вам может быть интересно прочитать http://wtfjs.com/, в котором есть записи для всей загрузки странных вещей, которые делает Javascript. Довольно многие из них связаны с null
и undefined
(знаете ли вы, что на самом деле можно переопределить значение встроенного объекта undefined
?!), и большинство из них имеют объяснение относительно что на самом деле происходит и почему. Было бы полезно показать вам, почему все работает так, как они делают, и определенно поможет показать вам все, чего нужно избегать! И если ничего другого, это делает хороший интересный читать, чтобы увидеть некоторые из злоупотреблений, которые люди пытались бросить на плохой язык.