作者回复: 赞的!就是这个意思。呵呵~
作者回复: Result是引用。
value是undefined。
value = GetValue(Result)
作者回复: 其实我早期也是这么理解的。好象大家理解事物的方式都差不多,就是从相似性出发,从差异性辨别。
但是我后来发现,与其如此,不如为新东西建个体系,然后在新体系中来看待这个新事物。这一下子就不同了。
以至于我现在对引用的认识,就不太依赖与比较或比拟。引用就是引用,它就是一个计算的结果,它存放结果中包括的那几个东西。它是一个数据结构,用在引擎层面来存储计算过程的中间信息,以及在连续计算中传递这些信息。
作者回复: 第2个不太完整。不过总体满分😃
第二个涉及的问题到20讲才开讲呢^_^
作者回复: 这个顺序是这样来读的(你仔细看看顺序是不是从左至右):
第一次
======
a.x = a = {n:2}
^1 ^2
第二次
======
a = { n: 2 }
^3 ^4
第三次
======
{ n: 2 }
^5 ^6
第四次(以下求值然后回传)
======
求值传回(4)
@4 <= ^5, ^6
第五次
======
求值回传(3)
@3 = (^4 <= ^5, ^6)
第六次
======
求值回传(2)
a = @3 = (^4 <= ^5, ^6)
第七次
======
求值回传(1)
a.x = a = @3 = ...
作者回复: 这个时候的Result是一个“引用(Reference)”。
如果它在后续运算中被作为lhs,例如 a.x = ...,那么它就是作为“引用”来使用,这样就可以访问到`x`这个属性,并置值;如果它在后续运算中被作为rhs,例如console.log(a.x),那么它就会被GetValue()取值(并作为值来使用),于是console.log()就能打印出它的值来。
a.x整体被作为“一个操作数”,它的用法与它被使用的位置是有关的。但是“得到它(亦即是对a.x这个表达式求Result)”的过程并没有什么不同。
你可以读一下这个“.”操作在ECMAScript中的规范:
https://tc39.es/ecma262/#sec-property-accessors-runtime-semantics-evaluation
作者回复: 这个……确实实现起来有难度。我通常在做讲演稿的时候才会用这种方式,但讲演稿的讲法,跟这里的课程的讲课方法区别还是很大的。
当然,即使不用动态的图,使用流程图或框线图其实也挺好的。不过,总之,以极客时间的“语音课程”来说,很难讲。——话说回来,如果是需要更深的阅读,以及更丰富的图例,以及表格等表现形式,那么可以看我的书哦。《JavaScript语言精髓与编程实践》这本书的第三版……快要出版了吔~ ^^.
作者回复: 我之前并没有听过关于这个 ‘未经计算的操作数’的说法。因此我特地地看了一下MDN中的相关说明。
‘未经计算的操作数(unevaluated operand)’这个,其实也并没有特别的难解。例如有一个值是2的常量x,对于这个`x`,如果它“计算了”,那结果当然就是2,对吧。那么这种情况下,“未经计算时的x”是什么呢?
这个其实还是我们在文章中说的“引用(规范类型)”。“引用(规范类型)”作为左手端时,只是引用,并不求值,这种情况下它就是`unevaluated operand`。所以,一个错误的、根本不存在的引用也可以被typeof操作,因为这个“错误的、根本不存在的”并没有被“计算”,所以也就不会抛出错误。例如你试试:
```
typeof adfasdljkfla; // <- 随便一个变量名
```
之所以没有异常发生,就是后面的`adfasdljkfla`这个东东“未经计算”。同样的,如果我们尝试下面的代码:
```
typeof(adfasdljkfla); // 同上例
```
这里其实多了一个操作符,就是一对括号表示的“分组运算符(grouping)”,这个运算符也是“返回未经运算的结果”。所以同样,不会出错。——在我们这个系列的文章中,这种情况称为“引用(规范类型)”,或者一个“(未决定操作手性rhs/lhs的)结果Result”。
还有你的另一个问题:
> 而且这个typeof的返回值,返回的应该不是一种类型吧!
这个是其它类型的语言来理解函数式语言的一个常见误区。尤其是,如果你以传统的(经典的)数据结构的知识为基础,那么更是会有误解。
在JavaScript中,以及在函数式语言中 ,“函数”的确是一种数据类型。它可以作为值(在函数界面上)传递,也可以作为结果(在函数返回中)传出,还可以查看类型,还可以与其它数据进行运算(例如 1 + (function(){})),那么它为什么不是“一种数据类型”呢?
函数既是数据,也是运算,这个是函数式语言的核心概念。
作者回复: “所以js engine,首先会create 一个新的属性x”。——这个操作是不会发生的。如果这样做,那么意味着“访问不存在的属性”是一个“创建属性”的行为,这样一来,系统的负担/开销就大得不得了了。
所以我在讲述这里的时候,说的是“空悬”了一个引用。就是这个意思。除了没有“创建属性”这样的隐式行为之外,你其它的描述是没什么问题的。
作者回复: 请参见对“青史成灰”的留言的回复。^^.
作者回复: 除了“a.x = a”导致栈异常之外,这个好象不太对。其它应该没什么问题了。
作者回复: https://tc39.es/ecma262/#sec-reference-specification-type
^^.
作者回复: 好主意!我问问编辑能怎么改。
后面的内容我尽量都加上。多谢提议!
作者回复: 你的整个理解过程是对的。
不过在案例3里面,“原来的e没有了因此不能赋值了”这个说法不对的。原来的e还是在的,在这个赋值操作中它也成功赋值了,只是赋在“原来的e”上面(所以我称为左侧“空悬”了一个e),而你又没有办法找到“原来的e”,所以看不见这个结果罢了。
你只需要为“原来的e”添加一个引用,就能观察到它了。比如声明为:
```
var x;
var e = x = {};
```
然后你能从x.a观察到“原来的e”的赋值效果。
这也是在这一讲里面讲到过的方法。^^.
作者回复: 对的。确实是这样。^^.
作者回复: 这个……这一节讲的不就是为什么么?这个例子就是一个应用了(当然我其实不建议这么用),所以答案还是得从这一课的内容中找。。。