删除不存在的字段的子字段

Deleting a subfield of a field that doesn't exist

本文关键字:字段 不存在 删除      更新时间:2023-09-26

我刚刚观察到以下奇怪的行为:

删除未定义的变量

> delete a
true
> 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 = {}
{}
> delete a.foo
true
> delete a.bar.something
TypeError: Cannot convert null to object
> a.bar
undefined

我有两个问题:

  • 为什么delete a在未定义a时有效?
  • 为什么删除a.bar.something抛出错误Cannot convert null to object而不是Cannot read property 'something' of undefined(因为a.bar undefined)?

根据文档 delete运算符从对象中删除属性,因此第一个问题的答案是a应该是this对象的属性?

在 c++ 应用程序中使用 delete a; 时,此错误出现(并且应该出现)error: ‘a’ was not declared in this scope

答案一分为二。前者没有描述太多,但回答了问题,而后者则进入了规范的细节。

博士

  1. 第一行有效,因为在非严格模式下,尝试删除变量只是有效的。
  2. 第一部分中的其余示例不起作用,因为未定义a
  3. delete a.foo有效,因为没有理由不应该这样做
  4. delete a.bar.something抛出是因为它首先尝试将a.bar变成对象,然后再尝试访问a.bar.something

现在要做一些完全不同的事情

首先,让我们明确两段代码在概念上是不同的,因为第一段讨论的是未声明的变量。

我们将研究如何指定delete

让我们从更容易理解的部分开始:

> 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语句的第3条:a是一个"未解决的引用",这是一种说"它没有声明"的奇特方式。Spec说在这种情况下简单地返回true

> a = {}
{}
> delete a.foo
true

正如您所期望的那样,这个很直观(可能),但无论如何让我们深入研究它。 delete obj.property进入第四条:

返回在提供GetReferencedName(ref)IsStrictReference(ref)作为参数ToObject(GetBase(ref))调用 [[Delete]] 内部方法的结果。

呜,这是一大堆无趣的东西。让我们忽略[[Delete]]部分之后的所有内容,只看如何指定[[Delete]]。如果我用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]] 内部方法的结果 [...]

我突出显示了与此特定片段相关的部分。在我们尝试delete任何东西之前,spec 告诉我们调用 ToObject(GetBase(ref))ref = a.bar.something 在哪里。那就这样吧!

  • GetBase (a.bar.something) === a.bar
  • ToObject (a.bar) === 抛出TypeErrorToObject(undefined)

这解释了最终的行为。

最后说明:您显示的错误消息具有误导性,因为它说它试图将null转换为对象,但它没有,因为它试图将undefined转换为一个对象。最新的chrome和Firefox抛出了一个更准确的版本,但我认为v8最近才修复了错误消息,所以也许升级到节点v11会显示正确的版本。

我正在分享实验和阅读材料,希望这会有所帮助!

1. 为什么要删除未定义的作品?

如果您尝试读取不存在的属性,JavaScript 会返回 "未定义"。这很方便,但如果您不这样做,则可以掩盖错误 小心,所以要注意错别字!

来源:http://www.w3.org/wiki/Objects_in_JavaScript

"Delete"将同时删除值和属性,因此只要 JavaScript 返回 a 的值,delete 的工作方式与此示例相同:

var a;
delete a;

此外,正如您所说athis对象的属性。您可以通过测试此代码来查看以下内容:

> var a = {}
undefined
> a
{}
> this.a
{}

2. 为什么删除 a.bar.something 会抛出错误 无法将 null 转换为对象而不是无法读取未定义的属性"某物"?

请看这些例子:

节点JS:

> var a = {}
undefined
> typeof a.b
'undefined'
> typeof a.b.c
TypeError: Cannot read property 'c' of undefined
    at repl:1:12
    at ......
> delete a.b.c
TypeError: Cannot convert null to object
    at repl:1:10
    at ......

Chrome 控制台:

> var a ={}
undefined
> typeof a.b
"undefined"
> typeof a.b.c
Uncaught TypeError: Cannot read property 'c' of undefined VM335:2
> delete a.b.c
Uncaught TypeError: Cannot convert undefined or null to object

如您所见,在测试typeof时,两者都会将a.b作为未定义的值进行管理。但是在删除时,chrome 说它可能是undefinednull,而 NodeJS 认为这是一个null值。

是否有可能 NodeJS 错误格式错误?