答案分为两部分。第一个描述不多,但回答了这个问题,而后者则进入了规范的基本细节。
tl;博士
- 第一行有效,因为在非严格模式下,尝试删除变量才有效。
- 第一部分中的其余示例不起作用,因为未定义
a
-
delete a.foo 有效,因为没有理由不应该这样做
-
delete a.bar.something 抛出,因为它首先尝试将 a.bar 转换为对象,然后再尝试访问 a.bar.something。
现在完全不同了
首先,让我们明确两段代码在概念上是不同的,因为第一段讨论的是一个未声明的变量。
我们会关注how delete is specified。
让我们从更容易理解的部分开始:
> delete a[0]
ReferenceError: a is not defined
> delete a.something
ReferenceError: a is not defined
> delete a.something[0]
ReferenceError: a is not defined
所有这些行都试图对 a 做一些事情,这是一个未声明的变量。因此,您会得到一个ReferenceError。到目前为止一切顺利。
> delete a
true
这进入了delete 语句的第三个子句:a 是一个“未解析的引用”,这是一种说“它没有被声明”的奇特方式。 Spec说在这种情况下简单地返回true。
> a = {}
{}
> delete a.foo
true
这很直观,正如您所期望的那样(可能),但无论如何让我们深入研究一下。 delete obj.property 进入第四条:
返回在ToObject(GetBase(ref)) 上调用[[Delete]] 内部方法的结果,提供GetReferencedName(ref) 和IsStrictReference(ref) 作为参数。
哎呀,这是一大堆无趣的东西。让我们忽略[[Delete]] 部分之后的所有内容,只看how [[Delete]] is specified。如果我用js写的话是这样的:
function Delete (obj, prop) {
var desc = Object.getOwnPropertyDescriptor(obj, prop);
if (!desc) {
return true;
}
if (desc.configurable) {
desc.magicallyRemove(prop);
return true;
}
throw new TypeError('trying to delete a non-configurable property, eh!?');
}
在示例中,a 没有名为 foo 的属性,因此没有发生任何特殊情况。
现在变得有趣了:
> delete a.bar.something
TypeError: Cannot convert null to object
发生这种情况是因为我们之前忽略了一些无趣的事情:
返回在 ToObject(GetBase(ref)) [...]
上调用 [[Delete]] 内部方法的结果
我突出显示了与此特定 sn-p 相关的部分。在我们尝试delete 之前,规范告诉我们调用ToObject(GetBase(ref)),其中ref = a.bar.something。那就这样吧!
-
GetBase(a.bar.something) === a.bar
-
ToObject(a.bar) === ToObject(undefined) 抛出 TypeError
这解释了最终的行为。
最后说明:您显示的错误消息具有误导性,因为它说它试图将null 转换为一个对象,但它没有尝试将undefined 转换为一个对象。最新的 chrome 和 firefox 抛出了一个更准确的版本,但我认为 v8 只是最近才修复了错误消息,所以升级到 node v11 可能会显示正确的版本。