22

因此,在对 javascript 中 null 和 undefined 的实现进行了激烈的争论/辩论/讨论之后,我希望有人解释实现背后的原因以及为什么它们在某些情况下会有所不同。我觉得一些特别的问题令人不安:

  • null == undefined评估为true
  • null + 1等于 1 但undefined + 1等于NaN
  • if(!null)计算为真,if(null)计算为假,但null == false计算为假。

我已经阅读了规范并且我知道结果是如何达到的,我正在寻找规范这是规范的范式和原因。其中一些点,尤其是第二点,考虑到第一点,感觉非常不一致。

4

4 回答 4

10

简短而甜蜜的版本是,JavaScript 由 Netscape 团队非常迅速地设计和实现,它有一些不一致之处,例如您指出的那些。

Internet Exploder 团队尽最大努力准确地复制 JS ,他们做得非常好,以至于不一致的地方也被复制了。当 Netscape 将 JS 标准化为 ECMAScript 时,MS 是其中的一部分,并且基本上说他们不允许更改标准,因为它会破坏旧代码(现有系统惯性)。不一致是标准化的,就是这样。

Douglas Crockford对其中一些问题进行了一系列很好的讨论

于 2011-08-09T18:16:51.890 回答
8

首先也是最重要的,虽然很多语言在没有两种方法用于类似目的的情况下就可以逃脱,但它们在 Javascript 中确实服务于不同但有些重叠的目的。“为什么两个都有?” 之前在这里被问过,我发现这个答案很好地解释了这一点。TL;DR : Javascript 有某些语言函数会产生缺失值而不是非初始化值:

  • delete'd 值
  • 对象中不存在的属性
  • 缺少函数参数

至于您问题中看似矛盾的地方,规范实际上很容易解释它们。 (我相信甚至可以说这种解释是优雅的,尽管可能有些人会强烈反对。)

分别解决每一项:

  • null == undefined 计算结果为 true

有关此问题的最佳解释,请参阅此答案。简而言之,抽象相等比较规范说它们(非严格)相等。


  • null + 1 等于 1 但 undefined + 1 等于 NaN

该运算符+用作一元 +(数字转换)运算符或加法运算符,但都将参数路由到ToNumber规范,该规范说:

参数类型 — 结果
未定义 — NaN
Null — +0
布尔值 — 如果参数为真,则结果为 1。如果参数为假,则结果为 +0。
Number — 结果等于输入参数(无转换)。

换句话说,null + 1成为+0 + 1undefined + 1成为NaN + 1,总是如此NaN


  • if(!null) 计算结果为 true, if(null) 计算结果为 false,但 null == false 计算结果为 false。

如您所知!Logical Not运算符,它对表达式执行ToBoolean 转换。它是一种截断。

if语句( )对exprif (expr)执行隐式布尔比较。所以看看上面两个语句中expr的类型:if

  • if (!null): expr!null哪个,给定逻辑非运算符 ( !) 的结果,是一个布尔值
  • if (null): expr isnull这意味着没有执行转换。

由于逻辑非运算符执行实际转换,因此在其他情况下也会发生同样的事情,并且实际上并不是您看起来的逻辑矛盾:

  • if (!"" == !undefined)=
  • if ("" == undefined)= false,当然。
于 2011-08-09T18:32:22.950 回答
4

最好将它们视为用于不同目的的完全不同的对象:

null用于“没有价值”。语言很少使用它,但宿主环境经常使用它来表示“没有价值”。例如,document.getElementById返回null不存在的元素。同样, s 的 IE-only 属性设置为onreadystatechange,not ,表示虽然该属性存在,但当前未设置。在您自己的代码中使用而不是 ,通常是一种很好的做法,并在以下情况下使用:HTMLScriptElementnullundefinednullundefinedundefined

undefined用于“甚至没有设置或根本不存在”。它在许多情况下是“默认值”,例如访问未定义的属性(例如在非 IE 浏览器中的 for)、没有语句的方法的默认返回值、onreadystatechange当函数被调用时的函数参数的默认值比它少声明等。HTMLScriptElementreturn

null通过这种方式,将其视为“有效值”是很有用的,只是一个表示特殊事物的值。而undefined更多的是语言层面的事情。

当然,在某些极端情况下,这些推理并不完全成立。这些主要是出于遗留原因。但是有一个区别,这是一个有一定意义的区别。


特别是对于你的痛点,它们大多来自于==操作符或类型强制的邪恶:

  • null == undefined:不要使用==运算符,因为它本质上是一堆在当时看起来很直观的向后兼容规则。
  • null + 1 === 1vs. undefined + 1 === NaN:运算符在评估之前+确实键入强制。Numbernull强制为0( +null === 0) 而undefined强制为NaN( isNaN(+undefined) === true)。
  • if (!null), if (null), null == false: if 评估其论点的“真实性”或“虚假性”,这与 . 的混乱规则无关==null是假的,并且!null是真实的,但是==不要让的规则null == false
于 2011-08-09T18:17:57.260 回答
1

null == undefined确实评估为真,但null === undefined评估为假。

这两个语句的区别在于相等运算符。Javascript 中的双等号将在比较它们之前将两个项目转换为相同的类型;对于null == undefined,这意味着null在完成比较之前将 转换为未定义的变量,因此相等。

我们可以用字符串和整数证明同样的效果:"12" == 12是真的,但是"12" === 12是假的。

此示例为我们提供了一种更简单的方法来讨论您的下一点,即为每个点添加一个。在上面的例子中,给整数加 1 显然会得到13,但是使用字符串"12" + 1会给我们一个字符串"121"。这是完全有道理的,你不会想要任何其他方式,但是使用双等号运算符,原始的两个值被报告为相等。

这里的教训是始终优先使用三等号运算符而不是双等号,除非您特别需要比较不同类型的变量。

你的最后一点表明了null一般的善变本质。这是一种奇特的野兽,任何尝试过使用可空数据库字段的人都会告诉你。Null 在计算机科学中有一个非常具体的定义,它在多种语言中以类似的方式实现,所以你描述的情况并不是一个特殊的 Javascript 怪异。空是奇怪的。不要期望它表现得像 的替代名称false,因为它不是那样工作的。内置infinity值可以以类似奇怪的方式表现,并且出于类似的原因。

不过,Javascript 确实有其怪异之处。您可能有兴趣阅读http://wtfjs.com/,其中包含 Javascript 所做的大量奇怪事情的条目。其中相当一部分与nulland相关undefined(您知道实际上可以重新定义内置undefined对象的值吗?!),其中大多数都附有对实际发生的情况和原因的解释。它可能有助于向您展示为什么事情会以它们的方式工作,并且肯定会有助于向您展示要避免的事情!如果不出意外的话,看看人们试图对糟糕的语言进行的一些滥用,这将是一个很好的娱乐阅读。

于 2011-08-09T18:31:23.703 回答