删除未定义的变量
> 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
我有两个问题:
>为什么在没有定义的时候删除作品?
>为什么删除a.bar.something会抛出错误无法将null转换为对象,而不能读取未定义的属性“something”(因为a.bar未定义)?
解决方法
TL;博士
>第一行工作原理是因为在非严格模式下,试图删除一个变量只是工作.
>第一节中的其余例子不起作用,因为没有定义
>删除a.foo的作品,因为没有理由不应该
>删除a.bar.something throws,因为它首先尝试在尝试访问a.bar.something之前将a.bar转换为对象.
现在对于完全不同的东西
首先,让我们明确地说,这两个代码段在概念上是不同的,因为第一个谈到一个没有声明的变量.
我们会在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
所有这些行都试图用一个没有声明的变量来做某事.所以你得到一个ReferenceError.到现在为止还挺好.
> delete a true
这就是删除声明的第3条:a是一个“未解决的引用”,这是一种说“没有被声明”的花哨的方式.规范说在这种情况下只是回归真实.
> a = {} {} > delete a.foo true
这个人的直观性,正如你所期望的(可能),但让我们深入其中.删除obj.property进入第4条款:
Return the result of calling the
[[Delete]]
internal method onToObject(GetBase(ref))
providingGetReferencedName(ref)
andIsStrictReference(ref)
as the arguments.
Welp是一大堆无趣的东西.让我们忽略[[删除]]部分之后的所有内容,只要看看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
这是因为我们以前忽略的一些不感兴趣的事情:
Return the result of calling the
[[Delete]]
internal method onToObject(GetBase(ref))
[…]
我突出显示了与此特定片段相关的部分.在我们尝试删除任何东西之前,spec告诉我们调用ToObject(GetBase(ref)),其中ref = a.bar.something.那我们来做吧
> GetBase
(a.bar.something)=== a.bar
> ToObject
(a.bar)===引用TypeError的ToObject(undefined)
这解释了最终的行为.
最后说明:您显示的错误消息是误导性的,因为它表示尝试将null转换为一个对象,它没有,因为它尝试将未定义转换为一个.最新的chrome和firefox抛出一个更准确的一个,但是我觉得v8最近才修复了错误信息,所以也许升级到节点v11会显示一个正确的版本.