Completion Record

Completion Record 表示一个语句执行完之后的结果,它有三个字段:

  • [[type]] 表示完成的类型,有 break continue return throw 和 normal 几种类型;
  • [[value]] 表示语句的返回值,如果语句没有,则是 empty;
  • [[target]] 表示语句的目标,通常是一个 JavaScript 标签。

    语句的分类

    语句 - 图1

    普通语句

    普通语句执行后,会得到 [[type]] 为 normal 的 Completion Record ,JavaScript引擎遇到这样的 Completion Record ,会继续执行下一条语句。

这些语句中,只有表达式语句会产生 [[value]] ,当然,从引擎控制的角度,这个 value 并没有什么用处。

Chrome 控制台显示的正是语句的 Completion Record 的 [[value]]。

语句块

语句块就是拿大括号括起来的一组语句,它是一种语句的复合结构,可以嵌套。
例如:

  1. {
  2. var x, y;
  3. x = 10;
  4. y = 20;
  5. }

语句块本身并不复杂,我们需要注意的是语句块内部的语句的 Completion Record 的 [[type]] 如果不为 normal ,会打断语句块后续的语句执行。

比如我们考虑,一个 [[type]] 为 return 的语句,出现在一个语句块中的情况。

return 语句可能产生 return 或者 throw 类型的 Completion Record 。我们来看一个例子。
先给出一个内部为普通语句的语句块:

  1. {
  2. var i = 1; // normal, empty, empty
  3. i++; // normal, 1, empty
  4. console.log(i); // normal, undefined, empty
  5. } // normal, undefined, empty

在每一行的注释中,我给出了语句的 Completion Record。
我们看到,在一个 block 中,如果每一个语句都是 normal 类型,那么它会顺次执行。接下来我们加入 return 试试看。

  1. {
  2. var i = 1; // normal, empty, empty
  3. return i; // return, 1, empty
  4. i++;
  5. console.log(i);
  6. } // return, 1, empty

但是假如我们在 block 中插入了一条 return 语句,产生了一个非 normal 记录,那么整个 block 会成为非 normal 。这个结构就保证了非 normal 的完成类型可以穿透复杂的语句嵌套结构,产生控制效果。

空语句

空语句就是一个独立的分号,实际上没什么大用。

  1. ;

空语句的存在仅仅是从语言设计完备性的角度考虑,允许插入多个分号而不抛出错误。

控制型语句

控制型语句带有 if 、 switch 关键字,它们会对不同类型的 Completion Record 产生反应。

控制类语句分为两部分,一类是对其内部造成影响,如 if 、switch 、while / for 、try 。另一类是对外部造成影响如 break 、 return 、 throw ,这两类语句的配合,会产生控制代码执行顺序和执行逻辑的效果,这也是我们编程的主要工作。

一般来说, for / while - break /continue 和 try - throw 这样比较符合逻辑的组合,是大家比较熟悉的,但是,实际上,我们需要控制语句跟 break 、continue 、 return 、 throw 四种类型与控制语句两两组合产生的效果。

break cotinue return throw
if 穿透 穿透 穿透 穿透
switch 消费 穿透 穿透 穿透
for/while 消费 消费 穿透 穿透
function 报错 报错 消费 穿透
try 特殊处理 特殊处理 特殊处理 消费
catch 特殊处理 特殊处理 特殊处理 穿透
finally 特殊处理 特殊处理 特殊处理 穿透

if 语句

switch 语句

  1. switch (num) {
  2. case 1:
  3. print 1;
  4. break;
  5. case 2:
  6. print 2;
  7. break;
  8. default:
  9. break;
  10. }

循环语句

  • while 和 do while
  • for 循环
  • for in 循环
  • for of 循环和 for await of 循环

for… in 循环可以用来遍历对象的可枚举属性列表
for…of 循环或返回值,可以直接应用在数组上,但是普通对象没有内置的 @@iterator ,所以无法自动完成 for…of 遍历。

with 语句

with 语句把对象的属性在它内部的作用域内变成变量。

  1. let o = {a:1, b:2};
  2. with(o) {
  3. console.log(a, b);
  4. }

带标签的语句

实际上,任何 JavaScript 语句是可以加标签的,在语句前加冒号即可:

  1. firstStatement: var i = 1;

大部分时候,这个东西类似于注释,没有任何用处。唯一有作用的时候是:与完成记录类型中的 target 相配合,用于跳出多层循环。

  1. outer: while (true) {
  2. inner: while (true) {
  3. break outer;
  4. }
  5. }
  6. console.log('finished')

break/continue 语句如果后跟了关键字,会产生带 target 的完成记录。一旦完成记录带了 target ,那么只有拥有对应 label 的循环语句会消费它。