• 海绵薇薇
    2019-11-19
    老师好,我又来了:-)

    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 }

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

    作者回复: 赞的!+1

    其实第1个问题的潜在问题是:这种情况下,x是什么呢?它显然是语法可以识别的东西,但如果这样,在语法上它是什么,且在执行环境中它又是什么?

    而第二个问题的答案,其实也会回到第一个问题上。如果是在严格模式上,第一个问题的答案是什么?并且,为什么它们不同?

    所以,呵呵,其实细一点的看,这两个问题还可以挖更多的呢。^^.

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

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

     4
     15
  • 海绵薇薇
    2019-11-22
    hello 老师好,感谢老师之前的回答:)
    突然想到,访问不存在的变量x报ReferenceError错误,其实是对x表达式的的Result引用做getValue的时候报的错误,然后为啥typeof x和delete x不报错,因为这两个操作没有求值。

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

    ^^.

     4
     11
  • 铭
    2019-11-11
    乍一读,云里雾里。翻了文档并做测试,总结如下:

    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了,而不需要细致地列举每一种情况。

     2
     10
  • 隔夜果酱
    2019-11-11
    既然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')()”的由来。

    ^^.

    
     10
  • SOneDiGo
    2019-11-22
    想问下老师如何理解用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”。

    
     8
  • 海绵薇薇
    2019-11-18
    感谢老师指点😁

    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()`)。

    
     8
  • 潇潇雨歇
    2019-11-16
    看的第三遍。还是要去看看规范加深理解。
    如果x根本不存在,delete x操作时,x首先是一个表达式,语义上是一个引用,然后去寻找该引用的result,但是x根本不存在,是找不到的。也就做不了什么,返回ture。
    如果obj.x是只读的或者不可配置的,表示他是不能删除的,但是他是实实在在的引用,是可以求值得到Result的,所以返回false。表示不能删除。

    作者回复: :)
    +1

    
     6
  • 余文郁
    2019-11-11
    老师,JS是基于对象的语言,不是面象对象的语言吧,感觉第二段这有点不妥,虽然ES6增加了class语法,但只是原型的语法糖而已

    作者回复: 在后面我会再着重地讲到JavaScript对面向对象的理解。

    如今我们对OOP的理解其实添加了太多应用的色彩。事实上,JavaScript对OOP的理解是很精彩、很学术,以及很完整的。不过这些内容大概要到第11讲之后了。

    至于“面向对象”还是“基于对象”,其实JavaScript 1.0是有类而无继承的,而JavaScript 1.1才开始使用原型来实现继承,这个时候它又抛弃了(严格意义上的)类。

    当然,上面看起来有点儿绕着你的问题在讲。所以,如果再确切地、准确无误地回复你的问题,那么应该是说:所谓面向对象的三个原则(封装、继承与多态),在严格意义上,后两者是多余的。所以不必过度去强调这些性质之于面向对象的重要性。

     3
     6
  • 仰望星空
    2019-11-11
    老师的英语发音delete偏差的有点多

    作者回复: 这个这个,惭愧呀惭愧~

    我的口语不是一点半点的糟糕(当然,其实不仅仅只是口语糟糕)。我尽量……注意……后面的课程~ 多谢多谢~

    惭愧呀~

    :(~

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

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

    
     4
  • 半橙汁
    2019-11-11
    在《你不知道JavaScript-上》中,看到过关于lhs和rhs的相关介绍,涉及到很多编译,语法解析的知识,真的很难肯...
    希望通过对老师专栏的学习,能够更加顺畅地去啃另外的中、下两本😂😂😂

    作者回复: 那三本书很不错!值得一读。^^.

    
     4
  • 渭河
    2019-11-21
    这句话要怎么理解呀
    所谓值类型中的字符串是按照引用来赋值和传递引用(而不是传递值)的

    作者回复: 这就是“传统中的‘引用’”用来解释这类现象的时候出现的麻烦。很典型的一个例子,话表达的是正确的,内容是正确的,说法也正确,就是特别特别难于理解。

    首先,“值类型中的字符串”是指什么呢?是指typeof(x) === 'string'中的那个`x`。在传统的javaScript概念中,这样的x是值类型,而不是引用类型。

    那么值“该怎么赋值和传递”呢?如果x的值是1,那么y = x的话,就是把1这个值“抄写”到y里面去。这是“正常的值”的处理方法,但是如果“字符串值”也这么处理,就完蛋了,因为字符串可能无数多个字符,那么当`y = x`按照“正常的值处理方法”来实现的话,这个“值的复制”的开销就受不了。

    所以:
    1. “值类型中的字符串”,是指照
    2. “引用来赋值和传递引用”的;且,
    3. 它是只传递引用(而不是传递值)的。

    如上。只是说起来特别麻烦而已。

    
     3
  • Mr_Liu
    2019-11-12
    思考题1:delete x x不存在返回的是true
    2: 删除会返回false,严格模式会报错
    第一遍听感觉有些云里雾里的感觉,又听了一遍加实践。但是有一点不理解或者不知道理解的对不对,希望老师解答一下
    问题一:
    例如var a = '123' delete a 返回的是false ,
    再次输入a 得到结果依然是 ‘123’,
    这是说明delete 没有起作用,其没有起作用的原因是因为 var a = '123' 中的a 是基本数据类型,不是引用类型,所以删除a 元素失败,借此印证了所讲的delete 删除的是表达式或者引用类型的结果。印证这句话的另一个例子是:
    var obj = {
       a: '123'
    },
    var b = obj.a
    delete b 返回false , 因为b = obj.a 属于一个赋值语句,b 也是个基本数据类型,所以也不起作用
    那么修改成
    var obj = {
    a: '123',
    b: {
        name: '123'
    }
    }
    var val = obj.b
    deletet val 返回的依然是false 后来会读了一下,有这样一句话:delete 其实只能删除一种引用,即对象的成员(Property)
    那么 delete x 还有什么存在的意义么。

    问题二:
    接着我使用delete obj.a 返回的是true ,再次输入 obj.a 返回的就是undefined
    但如果我使用
    var val = obj.b
    delete obj.b 返回的是true
    然后打印 obj.b 为undefined; val 为 {name: '123'}
    ,那老师的那句delete实际上是删除一个表达式的、引用类型的结果(Result),而不是在删除 x 表达式,或者这个删除表达式的值(Value)。是否可以理解为实际是是删除一直引用呢。
    展开

    作者回复: 问题1中,你的思考方向错了。`delete a`不起作用的原因是`var`声明导致的,而不是因为`a 是基本数据类型`。举例来说,

    `with (x = {a: 100}) delete a;`

    这个例子的结果中x.a是不存在的,但`a 也是基本数据类型`呀。所以是无关的。

    “delete x 还有什么存在的意义么”这个问题我之前回复过另一个留言,你找找。

    关于问题二,关键在于你所理解的“引用与值”,跟JavaScript内部所理解的“引用与值”是不一样的。也正是因此,我在这一讲的一开始用大量文字讨论了二者的区别。简单地来说,如果有表达式`x = x`,那么同一个变量`x`,在上述表达式中,左侧的这个是它的引用,左侧的是它的值。如果放在代码中看:

    x = 5; // 在JavaScript语言中,'5'是“值类型”
    x = x; // 在ECMAScript规范中,左侧是“引用x”,右侧是“值x”。

    我一直用“结果(Result)”来强调表达式“表达式计算的结果”,就是因为对于`x = x`来说,左侧和右侧都是表达式,左侧的结果是“lhs/引用(reference)”,而右侧的结果是“rhs/值(value)”。

    所以所谓“结果(Result)”,在不明确它的手性或用处之前,是二个意思都包含的。

     1
     3
  • 文全
    2019-11-11
    作为一个工作几年的前端,急需这部分底层原理
     2
     3
  • Smallfly
    2019-11-27
    在 ECMAScript 规范中,引用的构成至少需要 base value、referenced name、strict reference flag,具体引擎实现应该会把它们封装成一种数据结构来,从而来操作引用。

    而文中把 x = x 中的 x 叫做一个引用,应该不是很精确,x 只是引用的 referenced name。

    因为我们代码层面无法获取引用,也就没有名字,所以这里用 x 指代规范中的引用。

    请问老师这样理解对么?
    展开

    作者回复: > 在 ECMAScript 规范中,引用的构成至少需要 base value、referenced name、strict reference flag,具体引擎实现应该会把它们封装成一种数据结构来,从而来操作引用。
    在ECMAScript中,它就是一种数据结构啊。我们称byte/word/array/map为数据结构,为什么“Specification Types”就不是呢?你看ECMAScript规范里面,“Specification Types”就是在“Data Types”这一章中的一节啊。

    > 而文中把 x = x 中的 x 叫做一个引用,应该不是很精确,x 只是引用的 referenced name。
    不对。左侧的x就是引用,而不仅仅是referenced name。只是代码文本(在静态读代码的情况下)它是个名字x而已。

    > 因为我们代码层面无法获取引用,也就没有名字,所以这里用 x 指代规范中的引用。
    代码层面是可以获取所谓“引用”的,而且是“规范类型”中的引用。例如delete obj.x中的`obj.x`整体上就是一个引用。

    
     2
  • 杨尚坤
    2019-11-11
    写的很好

    作者回复: 多谢。我努力~努力~持续努力~

    ^^.

    
     2
  • balance
    2020-01-07
    
    (1) delete x
    (2) x=x
    (3) object. x()

    (1)中的x,(2)中等号左边的x,(3)中的x是同一种行态:表达式的result。老师,对吗?
    展开

    作者回复: 1和2都没有问题,3稍稍一点不同。

    在3里面,如果是“object.x”这个语法,那么这个`x`将是语法解析阶段得到的一个标识符名(IdentifierName),所以它不会视为“表达式的值”。但是,在执行“object.x”这个东西时,它确实是以“某个值”的形式传给“点号”这个运算符的。你可以理解为,“object.x”是指:opPropertyAccessor(object, IdentifierName_to_NameString(x))

    亦即是说,对于1和2来说,x是一个单值表达式,而在3中,x不是表达式。

    
     1
  • Smallfly
    2019-11-27
    看了其他同学的答案和老师的回答,感觉挺疑惑的,属性能否被 delete 是由 configurable 决定的,跟是否只读没有关系吧?

    const object1 = {};

    Object.defineProperty(object1, 'p', {
      value: 42,
      writable: false,
      configurable: true,
    });

    delete object1.p

    console.log(object1.p); // undefined
     
    展开

    作者回复: 是。writable不决定是否删除。属性表像一个数据库的表一样,可以增删查改,显然writable决定的是“改”,而不是“删”。

    
     1
我们在线,来聊聊吧