当前位置:嗨网首页>书籍在线阅读

05-异常处理和调用栈

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

11.4 异常处理和调用栈

一个标准的应用程序中会存在很多函数调用,而这些函数又会调用其他函数,继而调用更多的函数,以此类推。JavaScript解释器需要追踪这些函数调用。如果函数a调用了函数b,函数b调用了函数c,那么当函数c结束的时候,执行逻辑会返回到b,而当b结束的时候,执行逻辑则会返回到a。当c正在执行的时候,a和b都未“完成”。这种未完成的嵌套函数调用就叫作调用栈。

如果函数c出错了,那么a和b会怎样呢?一旦c出错,b也会出错(因为b可能会依赖c的返回值),这也会让a出错(因为a可能会依赖b的返回值)。本质上,错误会沿着调用栈传递,直到被捕获。

错误可以在调用栈中的任一级别被捕获,如果它们没有被捕获,JavaScript解释器就会强行终止程序。这种错误被称为未被处理的异常或者未被捕获的异常,它会使程序崩溃。由于程序中可能出错的地方太多了,想要捕获所有可能出现的错误不仅很困难,而且不现实,这也是程序会崩溃的原因。

当错误被捕获后,调用栈会提供一些用来诊断错误的有用信息。例如,如果函数a调用了函数b,b又调用了c,而c中出错了,此时调用栈不仅说明函数c中出错了,同时会说明错误是在被b调用的时候发生的,而这些都发生在被a调用的时候。当程序中有很多地方都调用了c的时候,这个信息就非常有价值。

在大多数JavaScript实现中,Error实例包含了一个stack属性,它是调用栈的字符串形式(它不是标准的JavaScript特性,不过却可以用在绝大多数环境中)。了解了这些知识后,可以写一个异常处理的例子演示这些功能:

function a() {
   console.log('a: calling b');
   b();
   console.log('a: done');
}
function b() {
   console.log('b: calling c');
   c();
   console.log('b: done');
}
function c() {
   console.log('c: throwing error');
   throw new Error('c error');
   console.log('c: done');
}
function d() {
   console.log('d: calling c');
   c();
   console.log('d: done');
} 
try { 
   a(); 
} catch(err) {
   console.log(err.stack);
}
try { 
   d(); 
} catch(err) {
   console.log(err.stack);
} 

在Firefox中运行这段代码,将会在控制台中看到以下输出:

a: calling b
b: calling c
c: throwing error
c@debugger eval code:13:1
b@debugger eval code:8:4
a@debugger eval code:3:4
@debugger eval code:23:4
d: calling c
c: throwing error
c@debugger eval code:13:1
d@debugger eval code:18:4
@debugger eval code:29:4

出现@符号的行是栈轨迹,它会从“最深层”函数开始(c),直到没有函数调用(浏览器自身)。可以看到,这里有两个不同的栈轨迹:一个显示了在b中调用了c,而b又被a调用,另一个则显示c直接被d调用。