delete 0会发生什么 | 青训营笔记

216 阅读5分钟

这是我参与【第四届青训营】笔记创作活动的第2天。

在JavaScript中同时支持值类型和引用类型的数据,且值类型中的字符串是按照引用来赋值和传递引用的。

什么是引用类型?

JavaScript中定义了“Object和Function就是引用类型”,所以说对象和函数就都是按引用来传递和使用的。

但是在Delete运算上这种解释就存在矛盾了。

delete 0
delete x

第一行代码是表示:删除一个为0的值 (情况一) 第二行代码是表示:既可能是删除一个值,也可能是删除一个引用(情况二)

但是JavaScript中又约定:那些在global对象上声明的属性,就“等同于”全局变量。 那么,第二行代码就还可能表示:删除的是一个global对象上的属性。(情况三)

这中间有哪些细节的区别呢? delete 这个运算的表面意思,是该运算试图销毁某种东西。然而,delete 0中的 0 是一个具体的、字面量表示的“值”。

JavaScript 认为“所有删除值的 delete 就直接返回 true”,表明该行为过程中没有异常。 因为在JavaScript1.2时代没有try..catch这样的异常处理,因此没有异常的情况下是通过返回true来表示。

返回值只表明执行过程中没有异常,但实际的执行行为是“什么也没发生”。

那么Delete到底删除了什么? 对于一门编译型语言来说,所谓“0”,就是上面所述的一个值,它可以是基础值(Primitive values),也可以是数值类型。但如果将这个问题上升到编译之前的、所谓语法分析的阶段,那么“0”就会被称为一个记号(Tokens)。一个记号是没有语义的,记号既可以是语言能识别的,也可以是语言不能识别的。唯有把这二者同时纳入语言范畴,那么这个语言才能识别所谓的“语法错误”。

delete 不仅仅是要操作 0 或 x 这样的单个记号或标识符(例如变量)。因为这个语法实际起作用的是一个对象的属性,也就是“删除对象的成员”。 那么它语法其实是:delete obj.x

只不过因为全局对象的成员可以用全局变量的形式来存取,所以它才有了:delete x这样的语法形式。

所以,delete是删除 x 这个成员,而不是删除 x 这个值。delete既然没办法表达异常,而 delete 0 又不产生异常,那么它自然就该返回 true。

obj.x 既不是之前说过的引用类型,也不是之前说过的值类型,它与typeof(x)识别的所有类型都无关。因为,它是一个表达式。 所以,delete 这个操作的正式语法设计并不是“删除某个东西”,而是“删除一个表达式的结果”:

表达式的结果又是什么? JavaScript中规定:所有一切表达式运算的目的就是得到一个值,Delete所有删除的就是这个表达式的计算结果值。

表达式的值,在 ECMAScript 的规范中,称为“引用”。 这是在JavaScript1.3中出现的概念,但是这个规范的影响力不大,所以开发人员还是把对象和函数成为引用,其他类型称为值。

再来看delete 0,实际上是表示:JavaScript 将 0 视为一个表达式,并尝试删除它的求值结果。

所以,现在这里的 0,其实不是值类型的数据,而是一个表达式运算的结果。而在进一步的删除操作之前,JavaScript 需要检测这个 Result 的类型:

  • 如果它是值,则按照传统的 JavaScript 的约定返回 true;
  • 如果它是一个引用,那么对该引用进行分析,以决定如何操作。

这个检测过程说明,ECMAScript 约定:任何表达式计算的结果(Result)要么是一个值,要么是一个引用。并且需要留意的是,在这个描述中,所谓对象,其实也是值。准确地说,是“非引用类型”。

例如:delete {} 删除的是一个字面量为{}的对象,当它被作为表达式执行的时候,结果也是一个值,然后,delete运算发现它的操作数是“值”,就直接返回了 true。

这就是delete 一个值的时候。

那delete引用的时候会发生什么?

在 JavaScript 的内部,所谓“引用”是可以转换为“值”,以便参与值运算的。因为表达式的本质是求值运算,所以引用是不能直接作为最终求值的操作数的。

这依赖于一个非常核心的、称为“GetValue()”的内部操作。所谓内部操作,也称为内部抽象操作(internal abstract operations),是 ECMAScript 描述一个符合规范的引擎在具体实现时应当处理的那些行为。GetValue()是从一个引用中取出值来的行为方法

所以说x=x,可以理解为 x = GetValue(x),含义即为把值x赋给引用x. 所以,“delete x”归根到底,是在删除一个表达式的、引用类型的结果(Result)

定义总结

  • delete 运算符尝试删除值数据时,会返回 true,用于表示没有错误(Error)。
  • delete 0 的本质是删除一个表达式的值(Result)。
  • delete x 与上述的区别只在于 Result 是一个引用(Reference)。
  • delete 其实只能删除一种引用,即对象的成员(Property)。

所以,只有在delete x等值于delete obj.x时 delete 才会有执行意义。例如with (obj) ...语句中的 delete x,以及全局属性 global.x。