Delete 0 是什么鬼?
在JavaScript中,delete操作的正式语法设计并不是“删除某个东西”,而是“删除一个表达式的结果”。
delete UnaryExpression
复制代码
那么表达式的结果又是什么呢?
表达式的结果是什么?
在JavaScript中,所有一切表达式运算的终极目的都是为了得到一个值(Result)。表达式的值Result,在 ECMAScript 的规范中,称为“引用”。
ECMAScript 约定:任何表达式计算的结果(Result)要么是一个值,要么是一个引用。
这里出现了两种“引用”, 一种是表达式Result是“引用”,还有一种是Result的值是一个“引用”。其实可以这么理解:把Result理解成表达式执行结果的“未决状态”。就是执行出结果来了,但没确定是作为lrs还是rhs,所以这种情况下,它就是未决的。当你确定了一个Result用作lrs,那么它就是引用(ECMAScript规范的引用);如果确定它用作rhs,那么它就是值(将由引擎隐式地调用GetValue()
)。
补充知识:LHS和RHS的含义是“赋值操作的左侧和右侧”并不一定意味这就是”=”的左侧和右侧。赋值操作还有其他几种形式,因此在概念上最好将其理解为“赋值操作的目标是谁(LHS)”以及“谁是赋值操作的源头(RHS)”。
那该怎么理解表达式在lrs的时候,Result就是引用,在rhs的时候Result就是值呢?咱们再举个例子。
x = x
复制代码
左右两边的x其实都是表达式,但是左边的x是引用,右边的x就是值,所以代码其实是这样滴
x = GetValue(x)
复制代码
翻译过来就是:把值 x 赋给引用 x。
那么delete在删除啥呢?
知道了Result,那再来看看delete操作。
比如
delete 0
复制代码
实际上是在说:JavaScript 将 0 视为一个表达式,并尝试删除它的求值结果(Result)。所以,现在这里的 0,其实不是值(Value)类型的数据,而是一个表达式运算的结果(Result)。
那么delete 0到底是在删除什么东西呢?0确实是没有任何的“引用”。往往这种未使用运算符的表达式,我们称为“单值表达式”。单值表达式的运算结果返回那个“对象字面量”的单值。然后,delete运算发现它的操作数是“值 / 非引用类型”,就直接返回了 true。什么也没有发生。
再比如
delete x
复制代码
delete x则是在删除一个表达式的、引用类型的结果(Result),而不是在删除 x 表达式,或者这个删除表达式的值(Value)。当 x 是全局对象 global 的属性时,所谓delete x其实只需要返回global.x这个引用就可以了。而当它不是全局对象 global 的属性时,那么就需要从当前环境中找到一个名为x的引用。
这些东西有啥用呢?
相信很多小伙伴们都写过obj.x
,它其实也是一个引用。“属性存取(”.”运算符)”返回一个关于“x”的引用,然后它可以作为下一个操作符(例如函数调用运算“()”)的左手端来使用,这才有了著名的“对象方法调用”运算:
obj.x()
复制代码
因为在对象方法调用的时候,函数 x() 是来自于obj.x这个引用的,所以这个引用将obj这个对象传递给 x(),这才会让函数 x() 内部通过 this 来访问到 obj。
根本上来说,如果obj.x只是值,或者它作为右手端,那么它就不能“携带”obj 这个对象,也就完成不了后续的方法调用操作。
对象存取 + 函数调用 = 方法调用
知识回顾
- delete 运算符尝试删除值数据时,会返回 true,用于表示没有错误(Error)。
- delete 0 的本质是删除一个表达式的值(Result)。
- delete x 与上述的区别只在于 Result 是一个引用(Reference)。
- delete 其实只能删除一种引用,即对象的成员(Property)。
思考题
- delete x 中,如果 x 根本不存在,会发生什么?
x表达式会返回一个引用,称为(UnresolvableReference)。而这一段逻辑在ECMAScript里面写的是“if IsUnresolvableReference, then return true”。也就是说,ECMAScript约定对于这种情况就是这么返回的,这属于规范约定(并且如果在这时发现是严格模式,就抛异常了。
- delete object.x 中,如果 x 是只读的,会发生什么?
因为x这个引用确实存在,只是因为属性为只读,所以会返回false,但是如果是严格模式那就会报错了。