01 | delete 0:JavaScript中到底有什么是可以销毁的
该思维导图由 AI 生成,仅供参考
习惯中的“引用”
- 深入了解
- 翻译
- 解释
- 总结
JavaScript中的delete运算符是一个元老级的语言特性,从JavaScript 1.2版本开始引入。它的操作涉及值类型和引用类型的数据,并且与类型的识别相关。在执行过程中,delete操作会返回true,表示没有异常发生。这篇文章深入解析了JavaScript中delete操作的行为,引出了表达式的结果和规范中的“引用”类型,为读者呈现了JavaScript中delete操作的技术特点。 文章介绍了JavaScript中的引用和值的转换,以及内部操作“GetValue()”从引用中取出值的行为。它还解释了对象属性存取和方法调用的关系,以及连续表达式运算实现新语法的示例。此外,文章还讨论了delete操作符尝试删除值数据时的行为,以及delete操作只能删除对象的成员(Property)的特点。 总的来说,这篇文章通过深入剖析JavaScript中delete操作的行为,展示了其操作涉及的技术特点,包括引用类型、内部操作和对象属性存取等内容。读者可以通过阅读本文快速了解JavaScript中delete操作的技术细节,对于理解JavaScript语言的底层原理和特性具有重要的参考价值。
《JavaScript 核心原理解析》,新⼈⾸单¥59
全部留言(97)
- 最新
- 精选
- 海绵薇薇老师好,我又来了:-) 1. delete 0 这里的0是一个值(就当前情况),而不是引用是吗? 2. delete x (x不存在) 返回true x 表达式返回的应该是一个引用,并且环境中并没有表示这个引用不能被删除,这个理解对吗? 但是文章中有提到delete只能删除属性这一种引用,糊涂了,估计这里的理解还是有问题。 3. delete null 返回true delete undefined 返回false 为啥啊?不都是值吗? 4. 还想知道昨天提问的1和2两条是不是漏洞百出啊,就想知道个结果😁。
作者回复: Oh~ 哈哈,你是说昨天有一个问题我只回复了3,没有回复1和2两条吗?那两条,是全对的,所以……嗯嗯,我只是没有回复确认而已。你对ECMAScript中的“引用规范类型”的使用场景和过程推演都是正确的。 关于今天的前3个问题,1是正确的。 2你也是对的。但是有一点,这个x的确会得到一个引用,称为(UnresolvableReference)。而这一段逻辑在ECMAScript里面写的是“if IsUnresolvableReference, then return true”。也就是说,ECMAScript约定对于这种情况就是这么返回的,这属于规范约定(并且如果在这时发现是严格模式,就抛异常了)。关于这里,你可以看一看: https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation 不过问题3,你倒是提到了一个“少有人知”问题,哈哈,这个问题我是漏讲了,而且其实还挺有趣、挺关键的。 是这样,早期的JavaScript中,undefined是一个特殊值,是在运行期中通过void运算,或者不返回值的函数,又或者一个声明了但未赋值的变量,等等类似这样的情况来“计算得到”的。所以在JavaScript的早期版本中,你没有办法直接判断“undefined是undefined”,例如无法写出“x === undefined”这样的代码,而你只能写类似“typeof(x) ==='undefined'”这样的代码。 后来(其实也没有太久),规范就约定把undefined作为可以缺省访问的名字,类似于null。但是这个时候就带来了一个矛盾,因为这个undefined很重要,早期的绝大多数框架或引擎都把它作为一个“全局名字”给声明了。也就是说,ECMAScript现在既没有办法将它规范成一个keyword,也没有办法处理成保留字等等,它看起来像null,但又没有办法在规范层面强制它。所以……ECMAScript就搞了一个“奇招”: > 我们把undefined声明成全局的属性,怎么样?! 嗯嗯,很好。所以你看,现在的引擎上面undefined看起来长得跟null值差不多,而且在ECMAScript规范中它们都还是平级的(是原始值),而且它们的作用也很接近,最后他们都还是从最初的JavaScript 1.x中就存在的概念,但是undefined/null两者却在实现上完全不同:undefined是一个全局属性,而null是一个关键字。 由于undefined是全局属性,所以`delete undefined`其实就是`delete global.undefined`,是删除引用,而不是删除值。而这个属性是只读的,所以就返回false了。 例如你可以试试下面的代码: > Object.getOwnPropertyDescriptor(global, 'undefined') { value: undefined, writable: false, enumerable: false, configurable: false }
2019-11-1911107 - 海绵薇薇hello 老师好,感谢老师之前的回答:) 突然想到,访问不存在的变量x报ReferenceError错误,其实是对x表达式的的Result引用做getValue的时候报的错误,然后为啥typeof x和delete x不报错,因为这两个操作没有求值。
作者回复: 强烈点赞!你这个就属于一通百通的例子。弄明白了Result用来做引用和值的方法/原理,一些具体现象就迎刃而解了! ^^.
2019-11-22968 - 潇潇雨歇1、如果x根本不存在,delete x什么也不做,返回true 2、如果x只读,delete object.x不能删除掉x属性,返回false;如果在严格模式下,会报错:TypeError: Cannot delete property 'c'
作者回复: 赞的!+1 其实第1个问题的潜在问题是:这种情况下,x是什么呢?它显然是语法可以识别的东西,但如果这样,在语法上它是什么,且在执行环境中它又是什么? 而第二个问题的答案,其实也会回到第一个问题上。如果是在严格模式上,第一个问题的答案是什么?并且,为什么它们不同? 所以,呵呵,其实细一点的看,这两个问题还可以挖更多的呢。^^.
2019-11-11433 - 潇潇雨歇关于delete的知识,大家可以看下MDN的讲解:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/delete 以及这篇深入delete博客:http://perfectionkills.com/understanding-delete/
作者回复: 谢谢 @潇潇雨歇
2019-11-11725 - SOneDiGo想问下老师如何理解用delete处理array element实际上在底层是如何操作的? 例如:array = [1,2,'1'] 为什么 delete array[2] 后数组就成了[1,2,undefined/empty]?
作者回复: 对于array来说,你理解为一个普通对象就可以了,只是一些array原型上的方法能帮助你处理array.length这个属性而已。 有多少个有效的element,那么就有多少个同名的(数字下标的)属性;而array.length记录着这个最大值。除了这一点,没有任何与其它对象不同。 所以你用array.pop()或array.push()等操作,甚至直接使用array[i]都可以影响到array.length这个属性——因为这些操作内部都会处理它。但是,你用delete去根本不会处理这个属性——因为delete是把array[i]当一个一般属性处理的,根本不知道array.length的存在。 例如: ``` > x = new Array(8) > x.length 8 > x[x.length] = 8 // add to last > x.length 9 > x.push(10) // push 10 > x.pop() // pop > x.length 9 > delete x[8] true > x.length 9 ``` 至于删除delete array[2],则array[2]位置上是undefined,这个与delete操作无关。而是因为你“读取一个不存在的属性,它的值就是undefined”。
2019-11-2220 - 海绵薇薇感谢老师指点😁 ref:语法上的引用 我又看了几遍文章并根据提供的连接,得出如下结论: 1. var x x = 0 console.log(x) x 表达式返回的是一个ref({referencedName: 'x', base: Environment Record}),然后计算值getValue(ref)得到具体的值,具体的值会分为传统意义上的基本类型和引用类型 2. 衍生出下面的猜想 var obj obj = {a: 1} console.log(obj.a) obj.a 也是一个ref({referencedName: 'a', base: obj}),然后计算值的时候getValue(ref)得到具体的值1 3. 关于表达式的结果Result的疑问。 文中说:表达式的值,在 ECMAScript 的规范中,称为“引用”。(表达式的结果(Result)是引用。) 但是后文说Result可能是引用/值。 这里的值我不能很好的理解。值指的是另一种引用的格式吗?例如链接文档中提到的base其实有很多种值 undefined, Object, a Boolean, a String, a Symbol, a Number。值指的是{base: 0}这种引用吗?如果不是这样的话base的Boolean等基本值类型有啥用啊? 还是说 0 这个表达式的Result就是0这个值? 期待老师的指点😁
作者回复: 关于3,我一般是用Result来表达它是表达式执行结果的“未决状态”。就是执行出结果来了,但没确定是作为lrs还是rhs,所以这种情况下,它就是未决的。 当你确定了一个Result用作lrs,那么它就是引用;如果确定它用作rhs,那么它就是值(将由引擎隐式地调用`GetValue()`)。
2019-11-18320 - 潇潇雨歇看的第三遍。还是要去看看规范加深理解。 如果x根本不存在,delete x操作时,x首先是一个表达式,语义上是一个引用,然后去寻找该引用的result,但是x根本不存在,是找不到的。也就做不了什么,返回ture。 如果obj.x是只读的或者不可配置的,表示他是不能删除的,但是他是实实在在的引用,是可以求值得到Result的,所以返回false。表示不能删除。
作者回复: :) +1
2019-11-16219 - Wiggle Wiggle即便 obj.x 是一个 function,当 obj.x 作为右手端时,也会被 GetValue 方法抽取出值来,而这个“值”并不是直觉上的数字或字符串。这里是有恍然大悟的感觉的,“值”和“引用”应当从严格的规范定义层面理解,而不能从直觉上来理解,只要满足定义,那就是“值”/“引用”。
作者回复: 赞的!就是这样!
2019-11-1216 - Ming乍一读,云里雾里。翻了文档并做测试,总结如下: delete 操作符用于删除对象的属性,它接收一个表达式,该表达式应返回对象属性的引用。 1. 如果表达式返回的结果是引用: 当该引用是 let 或 const 定义的,delete 执行结果总是 false; 当引用作为对象的属性不存在时,delete 对象的属性,执行结果为 true,表示未处理; 当该引用为 window 对象的属性且是 var 定义的,delete window 对象的属性,执行结果为 false,表示处理失败(获取属性描述符时为不可配置); 如果在全局环境下显示定义一个属性描述符为可配置的全局属性,执行 delete,结果是 true,表示操作成功; 当该引用为非 window 对象的属性且是 var 定义的,delete 非 window 对象的属性,执行结果为 true,表示处理成功(获取属性描述符时为可配置)。 2. 如果表达式返回的结果是值,如数字、字符串等,delete 执行结果为 true,表示未处理。
作者回复: 其实这一讲的核心是关于“引用/值”在ECMAScript规范类型中的使用与理解,而不是(不仅仅是)delete的使用。所以呢,解释delete这个操作的种种现象,最好是在ECMAScript规范所讨论的语言模型中来叙述,这样更容易讲得清楚。 比如说,`x`如果是一个属性(包括是global的属性),那么`delete x`的是否成功就取决于属性描述符,以及属性存取的过程(是否在严格模式中等等)。这样就Ok了,而不需要细致地列举每一种情况。
2019-11-11416 - 隔夜果酱既然delete这么鸡肋,只能删除对象的成员. 那么后来的版本中为什么不进行改进呢? 比如限定其只能用delete obj.x这种语法格式. 或者加入trycatch,对删除value的操作直接报错呢?
作者回复: 这个问题就牵扯得大了。 最早javascript中是没有明确、显式的global这个对象的,在宿主环境(例如浏览器中)你可以用window.x去访问它,这算是宿主在实现引擎的时候的约定。但是,仅只从引擎的角度上来说,既没有window,也没有global,更没有Global。所以,全局的变量虽然是作为全局属性名存在着,却没有办法写成global.x这样的引用。 而global这个全局名字,直到现在在ECMAScript中都还是个没被规范的东西。TC39有一个提案(https://github.com/tc39/proposal-global)专门来定义它,现在到了stage3,应该不会被否决了。但即使如此,这个东东也不叫global,而改名成了globalThis。——原本提案阶段是叫global的,但应用中有问题,所以就改了。 关于globalThis这个说法,又得是一段历史了。因为早期的JavaScript约定普通函数在“不作为对象方法调用”的时候,this值默认指向这个全局的global。所以,这也就是著名的代码“global = (new Function('return this')()”,或“global = Function('return this')()”的由来。 ^^.
2019-11-1114