Completion Record
Completion Record 表示一个语句执行完之后的结果,它有三个字段:
- [[type]] 表示完成的类型,有 break continue return throw 和 normal 几种类型;
- [[value]] 表示语句的返回值,如果语句没有,则是 empty;
- [[target]] 表示语句的目标,通常是一个 JavaScript 标签。
语句的分类
普通语句
普通语句执行后,会得到 [[type]] 为 normal 的 Completion Record ,JavaScript引擎遇到这样的 Completion Record ,会继续执行下一条语句。
这些语句中,只有表达式语句会产生 [[value]] ,当然,从引擎控制的角度,这个 value 并没有什么用处。
Chrome 控制台显示的正是语句的 Completion Record 的 [[value]]。
语句块
语句块就是拿大括号括起来的一组语句,它是一种语句的复合结构,可以嵌套。
例如:
{
var x, y;
x = 10;
y = 20;
}
语句块本身并不复杂,我们需要注意的是语句块内部的语句的 Completion Record 的 [[type]] 如果不为 normal ,会打断语句块后续的语句执行。
比如我们考虑,一个 [[type]] 为 return 的语句,出现在一个语句块中的情况。
return 语句可能产生 return 或者 throw 类型的 Completion Record 。我们来看一个例子。
先给出一个内部为普通语句的语句块:
{
var i = 1; // normal, empty, empty
i++; // normal, 1, empty
console.log(i); // normal, undefined, empty
} // normal, undefined, empty
在每一行的注释中,我给出了语句的 Completion Record。
我们看到,在一个 block 中,如果每一个语句都是 normal 类型,那么它会顺次执行。接下来我们加入 return 试试看。
{
var i = 1; // normal, empty, empty
return i; // return, 1, empty
i++;
console.log(i);
} // return, 1, empty
但是假如我们在 block 中插入了一条 return 语句,产生了一个非 normal 记录,那么整个 block 会成为非 normal 。这个结构就保证了非 normal 的完成类型可以穿透复杂的语句嵌套结构,产生控制效果。
空语句
空语句就是一个独立的分号,实际上没什么大用。
;
空语句的存在仅仅是从语言设计完备性的角度考虑,允许插入多个分号而不抛出错误。
控制型语句
控制型语句带有 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 语句
switch (num) {
case 1:
print 1;
break;
case 2:
print 2;
break;
default:
break;
}
循环语句
- while 和 do while
- for 循环
- for in 循环
- for of 循环和 for await of 循环
for… in 循环可以用来遍历对象的可枚举属性列表
for…of 循环或返回值,可以直接应用在数组上,但是普通对象没有内置的 @@iterator
,所以无法自动完成 for…of 遍历。
with 语句
with 语句把对象的属性在它内部的作用域内变成变量。
let o = {a:1, b:2};
with(o) {
console.log(a, b);
}
带标签的语句
实际上,任何 JavaScript 语句是可以加标签的,在语句前加冒号即可:
firstStatement: var i = 1;
大部分时候,这个东西类似于注释,没有任何用处。唯一有作用的时候是:与完成记录类型中的 target 相配合,用于跳出多层循环。
outer: while (true) {
inner: while (true) {
break outer;
}
}
console.log('finished')
break/continue 语句如果后跟了关键字,会产生带 target 的完成记录。一旦完成记录带了 target ,那么只有拥有对应 label 的循环语句会消费它。