因此,在javascript中对null和undefined的实现进行大讨论/辩论/讨论之后,我想让某人解释实现背后的原因以及它们在某些情况下的不同之处 . 我发现一些特别的问题令人不安:
-
null == undefined
评估为true
-
null + 1
等于1但undefined + 1
等于NaN
-
if(!null)
计算结果为true,if(null)
计算结果为false,但null == false
计算结果为false .
我已经阅读了规范并且我知道如何达到结果,我正在寻找规范这个规范的范例和原因 . 其中一些点,特别是第二点,给出第一点,感觉非常不一致 .
4 回答
简短而甜蜜的版本是Netscape团队非常迅速地设计和实现了JavaScript,它有一些不一致的地方,比如你已经指出的那些 .
Internet Exploder团队尽力完全复制JS,他们做得非常好,以至于不一致也被复制了 . 当Netscape将JS标准化时,因为ECMAScript MS是其中的一部分,并且基本上说他们不允许更改标准,因为它会破坏旧代码(现有系统惯性) . 不一致是标准化的,就是这样 .
Douglas Crockford有a very good series of talks about some of these issues .
First and foremost 虽然有很多语言没有两种方法可以实现这种类似的目的,但它们确实在Javascript中提供了不同但有些重叠的目的 . 之前有人问过"Why have both?",我发现this answer解释得相当好 . TL;DR :Javascript具有某些语言函数,这些函数生成缺少值而非非初始化值:
delete
'd值对象中不存在的属性
缺少函数参数
至于你问题中看似矛盾的问题,实际上很容易通过规范来解释 . (我相信甚至可以说这种解释很优雅,尽管可能有些人会强烈反对 . )
分别解决每个问题:
有关此问题的最佳解释,请参阅this answer . 简而言之,abstract equality comparison规范说它们(非严格地)相等 .
运算符
+
用作unary +(数字转换)运算符或addition运算符,但都将参数路由到ToNumber规范,其中说明:换句话说,
null + 1
变为+0 + 1
,undefined + 1
变为NaN + 1
,始终为NaN
.如您所知
!
是Logical Not运算符,它在表达式上执行ToBoolean conversion . 这是一种截断 .if statement(
if (expr)
)对expr执行隐式布尔比较 . 所以看看上面两个if
语句中的expr类型:if (!null)
:expr是!null
,给定逻辑非运算符(!
)的结果,它是 boolean .if (null)
:expr是null
,表示未执行转换 .由于 logical not 运算符执行实际转换,因此在其他情况下也会发生同样的事情,并且实际上并不像您看起来那样是逻辑矛盾:
if (!"" == !undefined)
= trueif ("" == undefined)
= false ,当然 .最好将它们视为用于不同目的的完全不同的对象:
null 用于"has no value."语言很少使用它,但主机环境经常使用它来表示"no value."例如,
document.getElementById
返回null
表示不存在的元素 . 类似地,HTMLScriptElement
的IE-only属性onreadystatechange
设置为null
,而不是undefined
,表示虽然该属性存在,但它当前未设置 . 通常的好做法是在您自己的代码中使用null
,而不是undefined
,并在以下情况下保留undefined
:undefined 用于"isn't even set or doesn't even exist."在很多情况下它是"default",例如访问未定义的属性(如非IE浏览器中的
HTMLScriptElement
为HTMLScriptElement
),没有return
语句的方法的默认返回值,调用函数时参数少于声明的函数参数的默认值等 .通过这种方式,将
null
视为"valid value,"只是表示一些特殊的东西是有用的 . 而undefined
更像是语言层面的东西 .当然,有一些边缘情况,这些推理并不完全成立;这些主要是出于遗产原因 . 但是存在差异,而且这是一个有意义的问题 .
至于你的痛点,它们主要来自于操纵者的邪恶或类型强制:
null == undefined
:不要使用==
运算符,因为它本质上是一堆后向兼容性规则,在当时看起来很直观 .null + 1 === 1
与undefined + 1 === NaN
:+
运算符在评估之前确实将强制类型设置为Number
. 并且null
强制执行0
(+null === 0
)而undefined
强制执行NaN
(isNaN(+undefined) === true
) .if (!null)
,if (null)
,null == false
:if评估其参数的"truthiness"或"falsiness",这与==
的乱七八糟的规则无关 .null
是假的,!null
是真的,但==
的规则不允许null == false
.null == undefined
确实评估为true,但null === undefined
评估为false .这两个语句的区别在于相等运算符 . 在比较它们之前,Javascript中的双重相等会将两个项目转换为相同的类型;对于
null == undefined
,这意味着在比较完成之前null
被转换为未定义的变量,因此是相等的 .我们可以用字符串和整数来表示相同的效果:
"12" == 12
为true,但"12" === 12
为false .这个例子为我们提供了一种更简单的方式来讨论你的下一点,即为每一点添加一个 . 在上面的例子中,向整数添加1显然给出了
13
,但是字符串"12" + 1
给了我们一个字符串"121"
. 这是完全合理的,你不会想要任何其他方式,但是使用双等运算符,原始的两个值被报告为相等 .这里的教训是始终使用三次等于运算符而不是双等式,除非您特别需要比较不同类型的变量 .
你的最后一点证明了
null
一般的变化无常的性质 . 它是一种奇特的野兽,因为任何人都希望它的行为像false
的替代名称,因为它不会那样工作 . 内置的infinity
值可以以类似的奇怪方式运行,并且出于类似的原因 .虽然Javascript确实有它的奇怪之处 . 您可能有兴趣阅读http://wtfjs.com/,其中包含Javascript所做的大量奇怪事情的条目 . 其中有一些与
null
和undefined
有关(你知道它实际上可以重新定义内置的undefined
对象的 Value 吗?!),其中大部分都会对实际发生的事情和原因进行解释 . 它可能有助于向您展示为什么事情按照他们的方式工作,并且肯定有助于向您展示要避免的事情!如果不出意外,它会让人看到一些有趣的阅读,以便看到人们试图用可怜的语言投掷的一些虐待行为 .