· 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。