null 与 undefined 及其在 JavaScript 中的行为
因此,在对 javascript 中 null 和 undefined 的实现进行了激烈的争论/辩论/讨论之后,我希望有人解释一下实现背后的原因以及为什么它们在某些情况下有所不同。我发现一些特别的问题令人不安:
null == undefined
计算结果为true
null + 1
等于 1 但undefined + 1
equalNaN
if(!null)
计算结果为 true,if(null)
计算结果为 false,但null == false 计算结果为 false。
我已经阅读了规范,并且知道如何达到结果,我正在寻找决定该规范的范式和原因。其中一些观点,尤其是第二点,考虑到第一点,感觉非常不一致。
So after a big argument/debate/discussion on the implementation of null and undefined in javascript I'd like somebody to explain the reasoning behind the implementation and why they differ in some circumstances. Some particular points I find troubling:
null == undefined
evaluates totrue
null + 1
equals 1 butundefined + 1
equalNaN
if(!null)
evaluates to true andif(null)
evaluates to false butnull == false
evaluates to false.
I've read the specification and I know how the results are reached, I'm looking for the paradigms and reasons that dictate this being the specification. Some of these points, especially the second one, given the first, feel very inconsistent.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
简短而甜蜜的版本是,JavaScript 是由 Netscape 团队非常快速地设计和实现的,并且它存在一些不一致之处,例如您所指出的那些。
Internet Exploder 团队尽最大努力完全复制 JS,而且他们做得非常好,以至于不一致的地方也被复制了。当 Netscape 将 JS 标准化为 ECMAScript 时,MS 是其中的一部分,并且基本上表示不允许他们更改标准,因为这会破坏旧代码(现有系统惯性)。不一致之处已标准化,仅此而已。
Douglas Crockford 有关于其中一些问题的一系列非常好的演讲。
The short and sweet version is that JavaScript was designed and implemented very rapidly by the Netscape team, and it had some inconsistencies such as the ones that you've pointed out.
The Internet Exploder team did its best to copy JS exactly and they did a damn good job of it to the point that the inconsistencies were copied as well. When Netscape went to get JS standardized as ECMAScript MS was a part of it and basically said that they weren't allowed to change the standard because it would break old code (existing systems inertia). The inconsistencies were standardized and that was that.
Douglas Crockford has a very good series of talks about some of these issues.
首先,也是最重要的,虽然很多语言没有两种方法来实现类似的目的,但它们在 Javascript 中确实具有不同但有些重叠的目的。 “为什么两者都有?”之前曾在这里被问过,我发现 这个答案解释了这一点相当好。 TL;DR:Javascript 具有某些语言函数,可以生成缺失值,而不是未初始化值:
删除
'd 值至于您的问题中看似矛盾的问题,实际上很容易通过规范来解释。 (我相信甚至可以说这个解释很优雅,尽管可能会有人强烈反对。)
分别解决每个问题:
请参阅此答案以获得最佳解释的这个。简而言之,抽象相等比较规范表明它们(非严格)相等。
运算符
+
充当 一元 +(数字转换)运算符或加法运算符,但两者都将参数路由到 ToNumber 规范,该规范表示:换句话说,
null + 1
变为+0 + 1
,undefined + 1
变为NaN + 1
,即始终NaN
。如您所知,
!
是 逻辑非 运算符,它对表达式执行 ToBoolean 转换。这是一种截断。if
语句 (if (expr)< /code>) 对 expr 执行隐式布尔比较。那么看一下上面两个
if
语句中 expr 的类型:if (!null)
: expr 是!null
,给定逻辑非运算符 (!
) 的结果,它是一个布尔值。if (null)
:expr 为null
表示未执行任何转换。由于逻辑非运算符执行实际转换,因此在其他情况下也会发生同样的情况,并且实际上并不像您看起来的那样是逻辑矛盾:
if (!"" = = !undefined)
= trueif ("" == undefined)
= false,当然。First and foremost, while plenty of languages get away without having two methods for such similar purpose, they do serve distinct though somewhat overlapping purposes in Javascript. "Why have both?" has been asked here before, and I find that this answer explains this fairly well. TL;DR: Javascript has certain language functions that produce absent values as opposed to non-initialized values:
delete
'd valuesAs for the seeming contradictions in your question, they are actually quite easily explained by the spec. (I believe it can even be argued that the explanation is elegant, though there are probably those who would vehemently disagree.)
Addressing each one separately:
See this answer for the best explanation of this. In short, the abstract equality comparison specification says that they are (non-strictly) equal.
The operator
+
functions as either the unary + (numeric conversion) operator or addition operator, but both route the arguments to the ToNumber spec, which says:In other words
null + 1
becomes+0 + 1
andundefined + 1
becomesNaN + 1
, which is alwaysNaN
.As you know
!
is the Logical Not operator, and it performs a ToBoolean conversion on the expression. It is a type of truncation.The
if
statement (if (expr)
) performs an implicit boolean comparison on expr. So take a look at the type of expr in both of the aboveif
statements:if (!null)
: expr is!null
which, given the result of the logical not operator (!
), is a boolean.if (null)
: expr isnull
which means no conversion was performed.Since the logical not operator performs an actual conversion, the same thing happens in other cases as well, and is not actually a logical contradiction as you seem it to be:
if (!"" == !undefined)
= trueif ("" == undefined)
= false, of course.最好将它们视为用于不同目的的完全不同的对象:
null
用于“没有价值”。该语言很少使用它,但主机环境经常使用它来表示“没有价值”。例如,document.getElementById
对于不存在的元素返回null
。同样,HTMLScriptElement
的 IE 专用属性onreadystatechange
设置为null
,而不是undefined
,以表示尽管该属性存在,但当前未设置。通常,最好在您自己的代码中使用null
而不是undefined
,并在以下情况下保留undefined
:< code>undefined 用于“甚至没有设置或根本不存在”。在许多情况下它是“默认”,例如访问未定义的属性(例如非 IE 浏览器中
HTMLScriptElement
的onreadystatechange
),不带return
语句、当使用比声明的参数少的参数调用函数时函数参数的默认值等。通过这种方式,将
null
视为一种“有效值”很有用,它只是一个表示特殊事物的值。而undefined
更多的是语言级别的事情。当然,在某些极端情况下,这些推理并不完全成立。这些主要是出于遗留原因。但还是有区别的,而且这种区别是有一定道理的。
特别是对于您的痛点,它们主要是由
==
运算符或类型强制的邪恶引起的:null == undefined
:不要使用==
运算符,因为它本质上是一堆当时看起来很直观的向后兼容规则。null + 1 === 1
与undefined + 1 === NaN
:+
运算符将类型强制转换为Number< /code> 在评估之前。
null
强制为0
(+null === 0
),而undefined
强制为NaN< /code> (
isNaN(+undefined) === true
)。if (!null)
、if (null)
、null == false
:if 评估其参数的“真实性”或“虚假性” ,这与==
的规则混乱无关。null
为假,!null
为真,但==
的规则不允许null == false.
They are best thought of as completely different objects used for different purposes:
null
is used for "has no value." It is used pretty rarely by the language, but often used by the host environment to signify "no value." For example,document.getElementById
returnsnull
for nonexistant elements. Similarly, the IE-only propertyonreadystatechange
forHTMLScriptElement
s is set tonull
, notundefined
, to signify that though the property exists, it's currently not set. It is generally good practice to usenull
in your own code, instead ofundefined
, and leaveundefined
for the following cases:undefined
is used for "isn't even set or doesn't even exist." It is the "default" in many cases, e.g. accessing an undefined property (likeonreadystatechange
forHTMLScriptElement
in non-IE browsers), the default return value from methods withoutreturn
statements, the default value of function parameters when the function is called with fewer arguments than it declares, and the like.In this way, it's useful to think of
null
as a "valid value," just one signifying something special. Whereasundefined
is more of a language-level thing.Sure, there are some edge cases where these reasonings don't entirely hold; those are mostly for legacy reasons. But there is a difference, and it's one that makes some deal of sense.
As for your pain points in particular, they mostly arise from the evil of the
==
operator or type coercion:null == undefined
: don't use the==
operator, because it essentially is a mess of backward-compatibility rules that seemed intuitive at the time.null + 1 === 1
vs.undefined + 1 === NaN
: the+
operator does type coercion toNumber
before evaluating. Andnull
coerces to0
(+null === 0
) whereasundefined
coerces toNaN
(isNaN(+undefined) === true
).if (!null)
,if (null)
,null == false
: if evaluates the "truthiness" or "falsiness" of its argument, which has nothing to do with the mess of rules for==
.null
is falsy, and!null
is truthy, but the rules for==
don't letnull == false
.null == undefined
确实计算结果为 true,但null === undefined
计算结果为 false。这两个语句的区别在于相等运算符。 JavaScript 中的双等于会在比较之前将两个项目转换为相同类型;对于
null == undefined
,这意味着null
在比较完成之前被转换为未定义的变量,因此相等。我们可以用字符串和整数演示相同的效果:
"12" == 12
为 true,但"12" === 12
为 false。这个例子为我们提供了一种更简单的方法来讨论你的下一个观点,即为每个观点添加一个。在上面的示例中,整数加 1 显然会得到
13
,但是使用字符串"12" + 1
会得到字符串"121"
>。这是完全有道理的,您不会想要任何其他方式,但使用双等于运算符时,原始两个值将被报告为相等。这里的教训是始终优先使用三等号运算符而不是双等号,除非您有特定需要比较不同类型的变量。
您的最后一点展示了
null
一般而言的变化无常的本质。这是一个奇怪的野兽,任何尝试过使用可空数据库字段的人都会告诉您。 Null 在计算机科学中有一个非常具体的定义,它在多种语言中以类似的方式实现,所以你描述的情况并不是特殊的 Javascript 怪异。空很奇怪。不要期望它的行为类似于false
的替代名称,因为它不是那样工作的。出于类似的原因,内置的无穷大值也可能以类似奇怪的方式表现。不过 Javascript 确实有其怪异之处。您可能有兴趣阅读http://wtfjs.com/,其中包含大量奇怪的事情的条目JavaScript 确实如此。其中相当一部分与
null
和undefined
有关(您知道吗,实际上可以重新定义内置undefined
的值)对象?!),其中大多数都对实际发生的情况及其原因进行了解释。它可能有助于向您展示事情为何如此运作,并且绝对有助于向您展示应避免的事情!如果不出意外的话,看看人们试图对这种蹩脚的语言进行的一些谩骂,读起来会很有趣。null == undefined
does indeed evaluate to true, butnull === undefined
evaluates to false.The difference in those two statements is the equality operator. Double-equal in Javascript will convert the two items to the same type before comparing them; for
null == undefined
, this means that thenull
is converted to an undefined variable before the comparison is done, hence the equality.We can demonstrate the same effect with strings and integers:
"12" == 12
is true, but"12" === 12
is false.This example gives us an easier way to discuss your next point, about adding one to each of them. In the example above, adding 1 to the integer obviously gives
13
, but with the string"12" + 1
gives us a string"121"
. This makes perfect sense, and you wouldn't want it any other way, but with a double-equal operator, the original two values were reported as equal.The lesson here is to always use the triple-equal operator in preference to the double-equal, unless you have a specific need to compare variables of different types.
Your final point demonstrates the fickle nature of
null
in general. It is a peculiar beast, as anyone who's ever tried to work with a nullable database field will tell you. Null has a very specific definition in computer science, which is implemented in a similar way across multiple languages, so the situation you describe is not a special Javascript weirdness. Null is weird. Don't expect it to behave like an alternative name forfalse
, because it doesn't work that way. The built-ininfinity
value can behave in a similarly bizarre way, and for similar reasons.Javascript does have its share of weirdness though. You might be interested in reading http://wtfjs.com/, which has entries for a whole load of strange things that Javascript does. Quite a few of them are to do with
null
andundefined
(did you know it's actually possible to redefine the value of the built-inundefined
object?!), and most of them come with an explanation as to what's actually happening and why. It might be helpful in showing you why things work the way they do, and will definitely helpful in showing you things to avoid! And if nothing else, it makes for a good entertaining read to see some of the abuses people have tried throwing at the poor language.