为什么 JavaScript 中有一个 `null` 值?

IT技术 javascript null language-features undefined
2021-01-18 05:14:27

在 JavaScript 中,有两个值基本上表示“我不存在” -undefinednull.

程序员未分配任何内容undefined的属性将是,但为了使属性成为nullnull必须显式分配给它。

我曾经认为需要null因为undefined是一个原始值和null一个对象。不是,即使typeof null会 yield 'object':实际上,两者都是原始值 - 这意味着既不能undefined也不null能从构造函数返回,因为两者都将被转换为空对象(必须抛出错误以声明构造函数失败)。

它们也在false布尔上下文中评估为我能想到的唯一真正的区别是,在数字上下文中,一个评估为NaN,另一个为0

那么为什么两者都存在undefinednull如果这只是让那些null在尝试找出是否已设置属性时错误检查的程序员感到困惑

我想知道的是,是否有人有一个合理的例子,其中有必要使用null而不能用undefined代替来表达

所以普遍的共识似乎是这undefined意味着“没有这样的财产”,而null意味着“该财产确实存在,但没有value”。

如果 JavaScript 实现实际上会强制执行此行为,我可以接受这一点 - 但它undefined是一个完全有效的原始值,因此可以轻松地将其分配给现有属性以破坏此合同。因此,如果要确定属性是否存在,则必须使用in运算符或hasOwnProperty()无论如何。所以再次:什么是用于分隔值的实际使用undefinednull

undefined当我想取消不再使用但我不想取消的属性值时,我实际上会使用delete我应该null改用吗?

6个回答

问题并不是真正的“为什么 JS 中有空值”——在大多数语言中都有某种类型的空值,它通常被认为非常有用。

问题是,“为什么JS 中未定义的值”。主要使用场所:

  1. 当您声明var x;但不分配给它时,x保持未定义;
  2. 当你的函数得到的参数少于它声明的参数时;
  3. 当您访问不存在的对象属性时。

null对于 (1) 和 (2)*,当然也同样有效。(3) 真的应该立即抛出一个异常,事实上它没有,而不是返回这个undefined稍后会失败的奇怪的东西,是调试困难的一个重要来源。

*:您也可以争辩说 (2) 应该抛出异常,但是您必须为默认/变量参数提供更好、更明确的机制。

然而,JavaScript 最初没有例外,也没有任何方式来询问对象是否有某个名称的成员——唯一的方法是(有时仍然是)访问该成员并查看您得到什么。鉴于null已经有一个目的并且您很可能希望为其设置一个成员,因此需要一个不同的带外值。所以我们有undefined,正如您指出的那样,这是有问题的,这是我们永远无法摆脱的另一个伟大的 JavaScript“功能”。

当我想取消设置不再使用但我不想删除的属性的值时,我实际上使用了 undefined 。我应该使用 null 吗?

是的。undefined当其他语言可能会抛出异常时,保留作为信号的特殊值。

null通常更好,除了在某些 IE DOM 界面上设置某些内容null可能会给您带来错误。通常在这种情况下设置为空字符串往往会起作用。

@bobince 早期版本的 JS 没有in操作符或hasOwnProperty? 因为它们比obj.hello !== undefined检查对象上是否存在属性更安全
2021-03-19 05:14:27
添加到您的“使用 [undefined] 的主要位置”列表中:(4) 当函数完成执行但未明确返回值时。
2021-04-02 05:14:27
没关系,我回答了我自己的问题。根据 MDN,它们都是在 ES3 中引入的。
2021-04-06 05:14:27
Args 是可扩展的。您可以随意插入任意数量的函数,并且一个函数可能会迭代并利用它们。可以随时为对象分配新属性。这些都是强大的功能,但我怀疑按照您的意愿抛出异常会带来很多开销。IMO,值得权衡。与通常在更严格的语言范式中进行转换相比,我在 JS 中检查是否存在的时间要少得多。
2021-04-08 05:14:27
有趣的是,一个问题的公认答案是如何以“这不是真正的问题......”开头的
2021-04-13 05:14:27

最好在这里描述,但总而言之:

undefined 是缺少类型和值,null 是缺少值。

此外,如果你在做简单的“==”比较,你是对的,结果是一样的。但是试试 ===,它比较类型和值,你会注意到不同之处。

@EricElliott:构造函数的返回值与虚假无关,但与对象性无关:如果返回值是一个对象,则返回它;如果它是像nullor这样的原始类型42,则丢弃返回值并返回新创建的对象
2021-03-16 05:14:27
我知道null !== undefined——我的问题是为什么需要两个表达相同语义概念的东西;另外,您的链接提到“null 是一个对象”-这是错误的,它是原始的...
2021-04-02 05:14:27
但仅此而已,它们不是相同的语义概念。当使用 == 表示相同的事情时,Null 会被强制,以方便程序员。
2021-04-03 05:14:27
它们不是相同的语义概念。至少对我来说,分配空值的属性和不存在的属性之间存在显着差异。
2021-04-06 05:14:27
@EricElliott:typeof谎言 - 阅读规范或尝试null从构造函数返回
2021-04-12 05:14:27

我认为没有任何理由同时拥有nulland undefined,因为许多人建议的唯一原因(“undefined意味着没有这样的变量/属性”)是无效的,至少在 JavaScript 中是这样。undefined无法告诉您变量/属性是否存在。

console.log(foo);               // "ReferenceError: foo is not defined"
                                // foo does not exist
var foo;
console.log(foo);               // "undefined", a different response
console.log(foo === undefined); // "true", but it does exist

var obj = {};
console.log(obj.hasOwnProperty("foo")); // "false", no such property
obj.foo = undefined;
console.log(obj.hasOwnProperty("foo")); // "true", it exists and has the value "undefined"
console.log(obj.foo === undefined);     // "true", but it does exist

obj.bar = "delete me";
obj.bar = undefined;
console.log(obj.hasOwnProperty("bar")); // "true", not actually deleted
delete obj.bar;
console.log(obj.hasOwnProperty("bar")); // "false", deleted

如您所见,检查foo === undefined不会告诉您是否foo存在,并且设置obj.bar = undefined实际上并没有删除bar

undefined应该代表“不存在”的可能是 JavaScript 作者的初衷然而,实施并没有变成那样。

这个例子实际上表明,如果你没有分配一个变量,它会返回undefined. 但是如果你确实分配undefined给它,它实际上不是undefined- 它是用它定义undefined并引用它的。undefined之间的唯一区别null是它们的用法和历史目的。它们都是原子的。
2021-03-19 05:14:27
@EricElliott 我认为你是对的。我已经编辑了我的答案。谢谢!
2021-03-31 05:14:27
在上面的代码示例中替换undefinednull,响应都是一样的。我不确定这如何回答这个问题。这是对别人的回答的评论吗?
2021-04-07 05:14:27
你应该编辑你的答案并实际说出来。如果你给出了那个答案,我可能会赞成。=)
2021-04-10 05:14:27
@EricElliott 我对这个问题的回答是,没有理由同时拥有nulland undefined,因为许多人建议的唯一原因是无效的。
2021-04-11 05:14:27

完全有可能两者都需要。例如,如果您查询 WMI,则完全有可能让类返回具有空值的属性。它们是被定义的,它们恰好在当时保持为空。

我认为你的 JavaScript 定义undefined为“没有这样的属性”和null“该属性没有value”的结论是完全正确的。在像 JavaScript 这样动态的语言中,这是一个非常重要的区别。鸭子类型的使用意味着我们需要能够区分不存在的属性和没有值的属性。它是我们获取类型信息的主要方式。在静态类型语言中,空字段和不存在的字段之间有明确的区别。在 JavaScript 中,这也不例外。然而,它在运行时被检查,并且可以修改直到那个时间。

我将不得不同意实施很奇怪,因为很多时候区别是模糊的。但是我认为在 JavaScript 中这种区别很重要。能够分配undefined是必不可少的。

我记得不久前读过一篇关于用 JavaScript 编写的在线 RPG 的博客文章。它使用了一些示例,其中对象被创建为现有实例的副本而不是原型(类、函数等),然后被更改。这确实让我明白了undefined修改现有对象时的强大功能,但我不记得是谁写的。

你能分享一下这个RPG吗?
2021-04-10 05:14:27