写过程序的同学都是到,有时候我们在熬夜调试一些代码有多么沮丧,最后当你发现问题是你忽略的简单逗号或类似的东西时,我都可以肯定会笑出声来。但是,客户报的bug将带来更多的皱眉而不是微笑。
话虽如此,错误可能令人烦恼,而背后却是真正的痛苦。有什么可以让用户体验好,不会看着像科幻里一样包一堆乱码,尤其是客户端,这就是我想解释一下JavaScript中称为try / catch的东西。
什么是JavaScript中的try/catch
try/catch块基本上是用来在JavaScript处理错误的。当你不希望脚本中的错误阻塞代码时,可以使用考虑使用它。
尽管看起来好像可以使用if语句轻松完成这些事情,但是try/catch 除了可以完成if/else语句可以完成的工作外,还为你带来很多好处。
try{
//...你的代码
}catch(e){
//...你的代码
}
使用try语句可以测试代码块是否存在错误,并且不会阻塞代码执行。例如:
try{
getListData() // getListData is not defined
}catch(e){
alert(e)
}
这就是构造try/catch的方式。你将代码放在try块中,如果出现错误,JavaScript会立即控制catch语句,并按照你的代码执行。在这种情况下,它会警告你该错误。
所有JavaScript错误实际上都是包含两个属性的对象:名称(例如,Error,syntaxError等)和实际错误信息。这就是为什么当e发出警报时,会得到类似ReferenceError的错误:undefined getListData。
与JavaScript中的所有其他对象一样,你可以决定以不同的方式显示错误提示,例如e.name(ReferenceError)和e.message。
但老实说,这与JavaScript的功能并没有真正的不同。那么,try / catch语句的好处是什么?
如何使用try/catch语句
throw
try/catch的优点之一是它能够显示你自己的自定义创建的错误。这称为抛错(throw error)。
在你不希望JavaScript显示这种晦涩难懂的情况下,可以使用throw语句抛出错误(异常)。该错误可以是字符串,布尔值或对象。如果有错误,catch语句将显示你抛出的错误。(用户就不会看到乱码了,这样用户的反感情绪也不会那么大了)
let num =prompt("输入一个大于30小于40的数字")
try {
if(isNaN(num)) {
throw new Error("不是一个数字(☉。☉)!");
}else if (num>40) {
throw new Error("数字大于40了");
}else if (num <= 30) {
throw new Error("数字小于30了");
}
}catch(e){
alert(e)
}
这是自定义的错误,这些我们在写代码的时候基本已经做了友好的判断了,更多的是我们在业务中无法预料的错误,通过将错误与JavaScript构造函数错误一起抛出,我们可以更进一步。
基本上,JavaScript将错误分为六类:
- EvalError - eval函数中发生错误。
- RangeError - 发生了超出范围的数字。
- ReferenceError - 使用尚未声明的变量
- SyntaxError - 出现语法错误时抛出;
- TypeError - 你使用的值超出预期类型的范围;
- URI(统一资源标识符)错误-如果你在URI函数中使用非法字符,则会引发URIError。
因此,有了这一切,我们很容易引发类似的错误throw Error。在这种情况下,错误的名称将为Error,并且消息catch里。你甚至可以继续创建自己的自定义错误构造函数,例如:
try {
getDataList();
} catch (e) {
if (e instanceof EvalError) {
alert(e.name + ":" + e.message);
} else if (e instanceof RangeError) {
alert(e.name + ": " + e.message);
}
}
而且,你可以轻松地在任何地方使用此功能throw new Error();
我们已经了解了try/catch及其如何防止脚本阻塞,但实际上取决于具体情况。让我们看下面这个例子:
try{
console.log({{}})
}catch(e){
alert(e.message)
}
console.log("最终的执行")
但是当你尝试使用try语句时,它仍然不起作用。这是因为JavaScript中有两种主要类型的错误;解析时错误
和运行时错误或异常
;
解析时错误
是代码内部发生的错误,主要是因为引擎无法解析代码。
例如,从上面看,JavaScript无法理解花括号的含义,因此,你的try/catch在这里没有作用。
运行时错误
是在有效代码中发生的错误,而这些正是try/catch必将发现的错误。
try{
test = x + 100;
} catch(e){
alert("x参数未定义")
}
alert("程序正常")
上面的代码是有效的,并且try/catch会捕获到运行时的错误。
Finally
Finally
标识没有错之后执行的代码,或者捕获错误后该怎么执行,他是你的try/catch块最后一条语句。
对于finally,你基本上是说,无论try/catch中发生什么(错误或没有错误),finally语句中的此代码都会运行。例如:
let test = '';
try{
if(data === "") {
throw new Error("test为空");
}else {
alert(`hello ${data}`);
}
} catch(e){
alert(e)
} finally {
alert("我时最后一条语句")
}
嵌套try块
你也可以嵌套try块,但是与JavaScript中的其他所有嵌套(例如if,for等)一样,它往往变得笨拙且不可读,因此,我建议你不要这样做。
嵌套try块为你提供了将一个catch语句用于多个try语句的优势。当然你也可以决定为每个try块编写一个catch语句,如下所示:
try {
try {
throw new Error('test');
} catch(e){
console.log(e)
} finally {
console.log('finally');
}
} catch (ex) {
console.log('ex error '+ex);
}
在这种情况下,外部try块不会有任何错误,因为它没有错。该错误来自内部try块,并且它已经处理了(它具有自己的catch语句)。
像这样面考虑的问题:
try {
try {
throw new Error('inner catch error');
} finally {
console.log('finally');
}
} catch (ex) {
console.log(ex);
}
上面的代码的工作原理略有不同:错误发生在内部try块中,没有catch语句,但是出现了finally语句。
需要注意的是try/catch语句可以用三种不同的方式来写:try…catch,try…finally,try…catch…finally,但错误是这种内在的。
这种内部尝试的最后声明肯定会起作用,因为就像我们前面说过的那样,无论try / catch中发生什么,它都可以起作用。但是,即使外部尝试没有错误,也仍然可以控制捕获以记录错误。甚至更好的是,它使用我们在内部try语句中创建的错误,因为该错误来自那里。
如果我们要为外部尝试创建一个错误,它将仍然显示所创建的内部错误,但内部错误会捕获到自己的错误。
你可以通过注释掉内部错误代码来使用下面的代码。
try {
try {
throw new Error('内部 error');
} catch(e){ //comment this catch out
console.log(e)
} finally {
console.log('finally');
}
throw new Error("外部 error")
} catch (ex) {
console.log(ex);
}
重新抛出错误
catch语句实际上捕获了所有发生的错误,有时我们可能不希望这样。例如,
"use strict"
let x=parseInt(prompt(" 请输入一个小于 5 的数字 "))
try{
y=x-10
if(y>=5) {
throw new Error(" y 不小于 5")
} else{
alert(y);
}
}catch(e){
alert(e)
}
假设输入的数字小于5(“使用严格”的目的是指示代码应在“严格模式”下执行)。在严格模式下,不能使用未声明的变量。
我希望try语句不会引发y的错误(当y的值大于5时,这几乎是不可能的)。上面的错误应该是因为y不小于输入的数字,并且不是y是undefined。
在这种情况下,你可以检查错误的名称,如果不是你想要的名称,则将其重新抛出:
"use strict"
let x = parseInt(prompt("请输入一个小于 5 的数字"))
try{
y=x-10
if(y>=5) {
throw new Error("YYYY");
}else{
alert(y);
}
}catch(e){
if(e instanceof ReferenceError){
throw e
}else alert(e)
}
这将简单地将错误重新抛出,以供另一个try语句在此处捕获阻塞脚本继续执行。当你只想监视特定类型的错误,而由于疏忽可能发生错误导致代码报错,此功能很有用。
适合使用try/catch的场景
因此,不要把判断 null ,undefined,这种都用try catch 。
而是尽量用在不可知的错误捕获上,而不是未知的错误。比如用来捕获宿主对象或者ECMAScript抛出的异常。
NodeJS里大多数error都是用来处理异常的,因为异常是不可避免的,例如数据库挂了,网络错误,你虽然知道有可能,但是不知道何时发生,这些异常你需要捕捉或者传给上层。而错误处理,则是一些基本的判定,可以从代码级别避免其发生,可预知可推测,如果发生了,不是系统问题,而是你的程序有bug了。
对于NodeJS来说,两种错误都时刻需要注意,特别是系统错误,因为不可预知,需要大量代码来catch错误,传递错误,最后统一处理。
结论
到这里我们的try/catch就解释完了,基本包含了常用到的场景。