精读ECMAScript规范:完成记录(Completion Record)

阅读: 评论:0

精读ECMAScript规范:完成记录(Completion Record)

精读ECMAScript规范:完成记录(Completion Record)

其实本来是想写很长的,但是后来读到阮一峰老师的《ES6标准入门》,感觉“前人之述备矣”,无心再写下去。姑且放上已经写好的这一节。

本文基于EMCA 2019规范。

之所以下定决心写这个东西,是因为今天遇到了一个问题。你肯定见过这样的语句(暂且先不考虑=====的问题,虽然我觉得一般都会用===):

let a = 0;
while (a == 10) { ++a; }

可能你也和我一样,曾经因为少写了一个等号而导致死循环:

let a = 0;
while (a = 10) { ++a; }

为此,有一些人建议把变量写在后面,因为10不是一个左值,所以一旦少写一个等号,就会报错;这样可以就在编译期发现错误了:

let a = 0;
while (10 = a) { ++a; } // Expression must be a modifiable lvalue

说了半天,还没说到主题;这只是一个引子。我不知道你有没有想过,为什么类似于while (a = 10) {}这样的语句会死循环?按理来说,while语句是在条件为真的情况下才会继续运行,所以,a = 10为真吗?或者循环结束的条件不是true这么简单?循环语句的工作原理是什么?

诸如此类的奇怪的问题还有很多,比如:

  • for (let i = 0; i < 10; ++i) {}
    i; // Uncaught ReferenceError: i is not defined
    for (var i = 0; i < 10; ++i) {}
    i; // 10
    
  • {} + [] // 0
    [] + {} // "[object Object]"
    
  • [] == ![] // true
    

也许你会觉得这种问题很无聊,记住就行了。我知道,网上流传着各种各样的解释,确实有很多人讲得通俗易懂,但是我觉得,无论如何,还是有必要稍微了解一下语言规范的;至少语言规范不太可能出错(如果出错,那未免有点太吓人了),更何况这些都可以在语言规范里找到答案。

其实,最主要的原因是,我始终觉得,无论别人讲得再好,也不如自己亲自去看看。用《东邪西毒》里的那句台词概括一下吧:

每个人都会经历这个阶段,看见一座山,就想知道山后面是什么。

切入正题。

完成记录

可能有的人已经知道,表达式是有“返回值”的。不知道你有没有留意过,在控制台输入表达式的时候,会有一个“返回值”出现,比如a = 10返回的是10:

a = 10; // 10

当然,这个值一般是无法获取到的。不过,有一个很不安全的方法可以获取到这个值,那就是eval

let b = eval('a = 10;');
b; // 10

eval的危害不必多说,我觉得你应该比我更清楚。所以,之前曾经有这么一个提案,就是所谓的“do表达式”,专门用来获取这个“返回值”的,可以让代码更加FP(函数式编程)一点:

let x = do {let tmp = f();tmp * tmp + 1
};

最主要的应用场景,可能还是像提出者所说的那样,应用在JSX上:

return (<nav><Home />{do {if (loggedIn) {<LogoutButton />} else {<LoginButton />}}}</nav>
)

扯远了。事实上,表达式的“返回值”不仅仅是一个值,而是一个对象。在规范里,这个“返回值”有一个专门的名字,叫完成记录(Completion Record)。看看原文是咋说的:

The Completion type is a Record used to explain the runtime propagation of values and control flow such as the behaviour of statements (break, continue, return and throw) that perform nonlocal transfers of control.

这个主要是强调了两点:

  1. 这个“返回值”是一个**记录(Record)**类型。记录类型是啥呢,按照规范里的说法,相当于就是一个用来描述数据类型的键值对,同时键名加上[[ ]],表示这是个内部属性。大概长成这样:

    { [[Field1]]: 42, [[Field2]]: false, [[Field3]]: empty 
    }
    

    当然了,这里的值都是抽象值,比如这个empty,不要误会成变量了。

  2. 这个“返回值”是用来描述运行时的值和控制流的。

刚才我们已经看到了记录的结构,完成记录作为一种特殊的记录,它的结构是这样的:

解释
[[Type]]normal, break, continue, return, 或throw完成的类型,用于描述控制流
[[Value]]任意值,或者为空(empty)产生的值
[[Target]]字符串,或者为空(empty)控制流的转移目标,有点像goto语句

在控制台显示的,应该就是它的[[Value]]的值。

本文发布于:2024-02-01 02:26:00,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170672556033188.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:ECMAScript   Completion   Record
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23