· 536 words · 3 min
用一个 try...catch 的例子引出 Completion Record:
function fn() {
try {
return 0;
} catch {
// do nothing
} finally {
return 1;
}
}
console.log(fn()) // 1
可以看到 try 中的 return 被 finally 中的 return 覆盖了,
在一个函数中执行了两次 return。这个现象是基于 JS 语句执行的完成状态 Completion Record。
Completion Record 表示一个语句执行完之后的结果,它有三个字段:
[[type]]:表示完成的类型。有 break、continue、return、throw 和 normal 几种。[[value]]:表示语句的返回值,没有则为 undefined。[[target]]:表示语句的目标,通常是一个 JS label。JS 根据语句的 Completion Record,在语句的复杂嵌套结构中,实现各种控制。
普通语句可分为声明类语句、表达式语句、空语句、debugger 语句。这些语句是从前往后按顺序执行的,
执行完之后会得到 [[type]] 为 normal 的 Completion Record,JS 引擎遇到这样的 Record 就会继续执行下一条语句。
这些语句中,只有表达式会有返回值,即产生 [[value]]。
控制台显示的是语句 Completion Record 的 [[value]]。
语句块是语句的复杂结构,可以嵌套。块内语句的 Completion Record 的 [[type]] 如果不为 normal 则会打断后续语句的执行。
// Completion Record([[type]], [[value]])
if(true) {
const i = 1; // normal, undefined
i++; // normal, 1
console.log(i); // normal, undefined
} // normal, undefined
加入了 return 之后:
// Completion Record([[type]], [[value]])
if(true) {
const i = 1; // normal, undefined
return i; // return, 1
i++;
console.log(i);
} // return, 1
加入了 return 之后,整个结构成为非 normal,穿透了语句嵌套,产生了控制效果。
控制型语句分为两部分,一种是对内部造成影响的比如 if、switch、while/for、try 等,
另一种是对外部造成影响的如 break、continue、return、throw 等。两种类型的语句相遇会产生不同的效果。
其中 try...catch 较为特殊,finally 中的内容必须保证执行,try...catch 执行完毕,
即使得到的结果是非 normal 的 Completion Record,也必须要执行 finally 中的内容,
并且会将 finally 执行完的 Completion Record 作为整个 try...catch...finally 的 Completion Record。