JavaScript 中的 delete 到底在删除什么?

213 阅读3分钟

最近又双叒决定重学 JavaScript,在看周爱民老师的专栏,借此机会记一些笔记。

我们都知道在 JavaScript 中存在一个操作符 delete,根据 MDN 中的定义,我们可以知道:

  • delete 删除的是对象中的某个属性
const obj = { foo: "bar" }
delete obj.foo
console.log(obj.foo) // undefined

但是实际上,我们发现,删除这个操作也是合法的

delete 0

如果你仔细想一想,你会发现这是不可能的事情。如果删除的是一个,那么 0 真的无法使用了吗?显然并不是的。

那么 delete 到底在删除什么?

想要知道这个问题,我们还是需要去看看 ECMAScript 规范

ECMAScript 规范:

关于 delete 的运行时语义(计算规则):

// delete + 一元表达式
delete UnaryExpression
  1. ref 成为计算 UnaryExpression 的结果

  2. ReturnIfAbrupt(ref)

  3. 如果 ref 不是一个引用记录(Reference Record),返回 true

  4. 如果 IsUnresolvableReference(ref) 返回 true,那么

    a. 如果 [[Strict]] 是 false

    b. 那么返回 true

  5. 如果 isPropertyReference(ref) 返回 true,那么

    a. 如果 IsPrivateReference(ref) 返回 false

    b. 如果 IsSuperReference(ref) 返回 true,抛出一个 Reference Error 异常

    c. 让 baseObjToObject(ref.[[Base]]) 的结果

    d. 让 deleteStatusbaseObj.[[Delete]](ref.[[ReferencedName]]) 的结果

    e. 如果 deleteStatusfalse,而且 ref.[[Strict]] 是 true,那么抛出一个 TypeError 异常

    f. 返回 deleteStatus

  6. 如果 isPropertyReference(ref) 返回 false

    a. 让 baseref.[[Base]]

    b. 断言 base 是否是环境记录(Environment Record)

    c. 返回 base.DeleteBinding(ref.[[ReferenceName]]) 的结果

注意 1: 如果 delete 是在严格模式下执行的,尾随的一元表达式如果是变量、函数参数、或者函数名的直接引用,那么会抛出一个 SyntaxError 异常

如果 delete 是在严格模式下执行的,如果删除的属性被配置为 { [[Configurable]]: false }(或者其他不能删除的情况),那么会抛出一个 TypeError 异常

关于规范的解释

乍一看,规范其实是非常复杂的,没关系,我们只需要看精华即可:

真正会出现操作的只有两个地方:

  • baseObj.[[Delete]](ref.[[ReferencedName]])
  • base.DeleteBinding(ref.[[ReferenceName]])

其他的部分,则都是根据不同的情况单纯的返回删除状态而已。

  1. 第一种操作是删除某个对象中的属性

  2. 第二种操作是在某个作用域中删除某个引用

得出结论

delete 操作符删除的不是一个,而是一个表达式的结果

  • 如果表达式是一个值,那么直接返回 true 例如:delete 0
  • 只有删除一个属性时 delete 操作符才会存在实际意义。
    • delete obj.x
    • delete x:x 是全局对象的属性
    • with 语句中的 delete x

有意思的特殊情况

1. 删除 null 和删除 undefined 的区别

delete null // true
delete undefined // false

我们知道,返回值是 deleteStatus,null 可以理解,它是一个,但是为什么 undefined 是 false 呢?

结果就是,由于历史原因,undefined 是全局对象的一个属性

// undefined
Object.getOwnPropertyDescriptor(window, null)
// { value: undefined, writable: false, enumerable: false, configurable: false }
Object.getOwnPropertyDescriptor(window, undefined)  

由于 undefined 设置了 configureable 为 false,那么根据规范无法删除,就会返回 false

不过根据上文,在严格模式下,删除 undefined 会报错(note 1.2)

2. 删除一个不存在的属性

delete is_not_exist // true

实际上,这个不存在属性会触发规范 4,即 IsUnsolvableReference,所以不会抛出异常,还是会返回 true

不过根据上文,在严格模式下,删除一个不存在的属性会报错(4.a)。