JavaScript 核心原理解析
周爱民
《JavaScript 语言精髓与编程实践》作者,南潮科技(Ruff)首席架构师
32699 人已学习
新⼈⾸单¥59
登录后,你可以任选3讲全文学习
课程目录
已完结/共 28 讲
开篇词 (1讲)
JavaScript 核心原理解析
15
15
1.0x
00:00/00:00
登录|注册

01 | delete 0:JavaScript中到底有什么是可以销毁的

delete object.x中x是只读的情况
delete x中x不存在的情况
delete运算符的行为总结
对象属性存取和方法调用
delete运算对引用的操作
左手端和右手端的区别
GetValue()操作
引用类型在开发人员中的理解
ECMAScript规范中的引用类型
引用类型的结果
表达式的值
删除值和删除属性的区别
delete操作的结果
对象和函数作为引用类型
JavaScript中的引用类型
delete运算与typeof运算的关系
JavaScript 1.2中引入delete运算
思考题
知识回顾
delete运算的应用
引用和值的转换
规范中的“引用”
表达式的结果
delete运算的行为
引用类型的概念
delete运算的历史
JavaScript中的delete运算
JavaScript中的delete运算

该思维导图由 AI 生成,仅供参考

你好,我是周爱民,感谢你来听我的专栏。
今天是这个系列的第一讲,我将从 JavaScript 中最不起眼的、使用率最低的一个运算——delete 讲起。
你知道,JavaScript 是一门面向对象的语言。它很早就支持了 delete 运算,这是一个元老级的语言特性。但细追究起来,delete 其实是从 JavaScript 1.2 中才开始有的,与它一同出现的,是对象和数组的字面量语法。
有趣的是,JavaScript 中最具恶名的 typeof 运算其实是在 1.1 版本中提供的,比 delete 运算其实还要早。这里提及 typeof 这个声名狼藉的运算符,主要是因为 delete 的操作与类型的识别其实是相关的。

习惯中的“引用”

早期的 JavaScript 在推广时,仍然采用传统的数据类型的分类方法,也就是说,它宣称自己同时支持值类型和引用类型的数据,并且,所谓值类型中的字符串是按照引用来赋值和传递引用(而不是传递值)的。这些都是当时“开发人员的概念集”中已经有的、容易理解的知识,不需要特别解释。
但是什么是引用类型呢?
在这件事上,JavaScript 偷了个懒,它强行定义了“Object 和 Function 就是引用类型”。这样一来,引用类型和值类型就给开发人员讲清楚了,对象和函数呢,也就可以理解了:它们按引用来传递和使用。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
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-19
    11
    107
  • 海绵薇薇
    hello 老师好,感谢老师之前的回答:) 突然想到,访问不存在的变量x报ReferenceError错误,其实是对x表达式的的Result引用做getValue的时候报的错误,然后为啥typeof x和delete x不报错,因为这两个操作没有求值。

    作者回复: 强烈点赞!你这个就属于一通百通的例子。弄明白了Result用来做引用和值的方法/原理,一些具体现象就迎刃而解了! ^^.

    2019-11-22
    9
    68
  • 潇潇雨歇
    1、如果x根本不存在,delete x什么也不做,返回true 2、如果x只读,delete object.x不能删除掉x属性,返回false;如果在严格模式下,会报错:TypeError: Cannot delete property 'c'

    作者回复: 赞的!+1 其实第1个问题的潜在问题是:这种情况下,x是什么呢?它显然是语法可以识别的东西,但如果这样,在语法上它是什么,且在执行环境中它又是什么? 而第二个问题的答案,其实也会回到第一个问题上。如果是在严格模式上,第一个问题的答案是什么?并且,为什么它们不同? 所以,呵呵,其实细一点的看,这两个问题还可以挖更多的呢。^^.

    2019-11-11
    4
    33
  • 潇潇雨歇
    关于delete的知识,大家可以看下MDN的讲解:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/delete 以及这篇深入delete博客:http://perfectionkills.com/understanding-delete/

    作者回复: 谢谢 @潇潇雨歇

    2019-11-11
    7
    25
  • 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-22
    20
  • 海绵薇薇
    感谢老师指点😁 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-18
    3
    20
  • 潇潇雨歇
    看的第三遍。还是要去看看规范加深理解。 如果x根本不存在,delete x操作时,x首先是一个表达式,语义上是一个引用,然后去寻找该引用的result,但是x根本不存在,是找不到的。也就做不了什么,返回ture。 如果obj.x是只读的或者不可配置的,表示他是不能删除的,但是他是实实在在的引用,是可以求值得到Result的,所以返回false。表示不能删除。

    作者回复: :) +1

    2019-11-16
    2
    19
  • Wiggle Wiggle
    即便 obj.x 是一个 function,当 obj.x 作为右手端时,也会被 GetValue 方法抽取出值来,而这个“值”并不是直觉上的数字或字符串。这里是有恍然大悟的感觉的,“值”和“引用”应当从严格的规范定义层面理解,而不能从直觉上来理解,只要满足定义,那就是“值”/“引用”。

    作者回复: 赞的!就是这样!

    2019-11-12
    16
  • 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-11
    4
    16
  • 隔夜果酱
    既然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-11
    14
收起评论
显示
设置
留言
97
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部