Javascript错误处理——try...catch
2017-07-29 21:22
686 查看
Javascript错误处理——try…catch
无论我们编程多么精通,脚本错误怎是难免。可能是我们的错误造成,或异常输入,错误的服务器端响应以及无数个其他原因。通常,当发送错误时脚本会立刻停止,打印至控制台。
但
try...catch语法结构可以捕获错误并可以做更多的事情,而不是直接停止。
“try…catch” 语法
try...catch结构有两个语句块,即
try,然后
catch:
try { // code... } catch (err) { // error handling }
工作流程如下:
首先
try{...}代码块执行。
如果没有错误,那么
catch(err)被忽略:执行到
try结尾时,跳过
catch块。
如果发生错误,那么
try块中执行停止,控制流进入
catch(err).
err(可以是任何名称)变量包含错误发生相关的信息信息对象。
所以,
try{...}块内的错误不会让脚本停止:我们有机会在
catch块内处理。让我们看更多的示例。
无错误示例:显示
alert (1)和
(2):
try {
alert(‘Start of try runs’); // (1) <–
// …no errors here
alert(‘End of try runs’); // (2) <–
} catch(err) {
alert(‘Catch is ignored, because there are no errors’); // (3)
}
alert(“…Then the execution continues”);
带错误示例:显示
(1)和
(3):
try {
alert(‘Start of try runs’); // (1) <–
lalala; // error, variable is not defined!
alert(‘End of try (never reached)’); // (2)
} catch(err) {
alert(
Error has occured!); // (3) <–
}
alert(“…Then the execution continues”);
try..catch仅作用在运行时错误
对
try..catch,代码必须是运行时,换句话说,必须是有效的Javascript代码。
如果代码语法错误不会工作,举例,下面不会捕获大括号错误:
try { {{{{{{{{{{{{ } catch(e) { alert("The engine can't understand this code, it's invalid"); }
Javascript引擎首先读代码,然后运行。发生在读阶段错误称为“解析时”错误,不可恢复,因为引擎不理解代码。
所以,
try...catch仅能处理有效代码中的错误,被称为“运行时”错误,有时也称为“异常”。
try..catch同步执行
如果在预定的(scheduled)代码发生异常,如
setTimeout,那么
try...catch不捕获异常:
try { setTimeout(function() { noSuchVariable; // script will die here }, 1000); } catch (e) { alert( "won't work" ); }
因为
try...catch包装了
setTimeout调用预定函数。但函数会延后执行,此时引擎已经立刻了
try...catch结构。
为了捕获预定执行函数内的异常,
try...catch必须在函数内部:
setTimeout(function() { try { noSuchVariable; // try..catch handles the error! } catch (e) { alert( "error is caught here!" ); } }, 1000);
错误对象
当错误发生时,Javascript生成包含细节信息的对象,并作为参数传递给catch块:
try { // ... } catch(err) { // <-- the "error object", could use another word instead of err // ... }
对所有内置错误,
catch内的错误对象主要有两个属性:
name
错误名称,一个未定义变量是
“ReferenceError”。
message
错误信息的文本描述。
在大多数环境中有其他非标准属性,被广泛使用和支持的一个是:stack
当前调用栈:关于导致错误的嵌套调用序列,用于调试目的。
举例:
try { lalala; // error, variable is not defined! } catch(err) { alert(err.name); // ReferenceError alert(err.message); // lalala is not defined alert(err.stack); // ReferenceError: lalala is not defined at ... // Can also show an error as a whole // The error is converted to string as "name: message" alert(err); // ReferenceError: lalala is not defined }
使用“try…catch”
让我们探索一个真实的用例:
我们知道,Javascript支持方法
JSON.parse(str),用于读json值。通常用于解析从网络中接收json数据,如服务器端或其他来源。接收并调用
JSON.parse,如下:
let json = '{"name":"John", "age": 30}'; // data from the server let user = JSON.parse(json); // convert the text representation to JS object // now user is an object with properties from the string alert( user.name ); // John alert( user.age ); // 30
如果
json非标准的,JSON.parse产生错误,脚本为停止。这样我们会满意吗?当然不会!
如果数据带有某种错误,用户完全不知道发送什么(除非打开开发控制台)。没人喜欢发生错误时脚本停止且没有任何错误信息。
让我们使用
try...catch处理错误:
let json = "{ bad json }"; try { let user = JSON.parse(json); // <-- when an error occurs... alert( user.name ); // doesn't work } catch (e) { // ...the execution jumps here alert( "Our apologies, the data has errors, we'll try to request it one more time." ); alert( e.name ); alert( e.message ); }
这里我们使用
catch块仅显示信息,也可以做更多:新的网络请求,建议另一种选择,发送错误信息至日志等,总之都比代码直接停止好。
抛出我们自己的错误
如果json语法正在,但没有需要的name属性,会怎么样?
如下:
let json = '{ "age": 30 }'; // incomplete data try { let user = JSON.parse(json); // <-- no errors alert( user.name ); // no name! } catch (e) { alert( "doesn't execute" ); }
这里
JSON.parse执行正常,但缺少name属性,实际对我们来说是个错误。为了统一错误处理,我们需要使用
throw操作。
Throw操作
该操作产生一个错误。语法:
throw <error object>
技术上,可以使用任何内容作为错误对象。可以是原始类型,如数字或字符串,但最好使用对象,并带有name和message属性(与内置错误对象兼容)。
Javascript有很多内置标准错误构造器:Error、SyntaxError、ReferenceError、TypeError等其他。我们也能使用他创建错误对象。
语法:
let error = new Error(message); // or let error = new SyntaxError(message); let error = new ReferenceError(message); // ...
对内置错误对象(仅为错误对象),name属性正好是构造函数的名称,message是构造函数参数。
let error = new Error("Things happen o_O"); alert(error.name); // Error alert(error.message); // Things happen o_O
让我们看
JSON.parse生成的这种错误:
try { JSON.parse("{ bad json o_O }"); } catch(e) { alert(e.name); // SyntaxError alert(e.message); // Unexpected token o in JSON at position 0 }
如我们所见,错误为:
SyntaxError。
在我们的示例中,缺省name属性,也可以视为语法错误,假设users必须有个name属性,所以我们抛出错误:
let json = '{ "age": 30 }'; // incomplete data try { let user = JSON.parse(json); // <-- no errors if (!user.name) { throw new SyntaxError("Incomplete data: no name"); // (*) } alert( user.name ); } catch(e) { alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name }
在星号行,
throw操作产生
SyntaxError错误,并带有给定的
message,与Javascript自身生成的错误一致。try块中的执行立刻停止,控制流跳至
catch块。
现在
catch变成了独立处理所有错误的块:
JSON.parse和其他错误。
再次抛出错误
上面的示例,我们使用
try...catch处理不正确的数据,但也可能是其他异常发生在
try...catch块中,如变量未定义或其他,不仅是“不正确的数据”。
如下:
let json = '{ "age": 30 }'; // incomplete data try { user = JSON.parse(json); // <-- forgot to put "let" before user // ... } catch(err) { alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined // (not JSON Error actually) }
当然,一切都是可能的!程序人员造成的错误,即使被大量使用的开源工具——可能会突然发现一个疯狂的bug,导致了可怕的黑客攻击(就像使用ssh工具发生的那样)。
在我们的示例中,
try...catch是为了处理不正确数据错误,但实际上,catch捕获try块中所有的错误。如有一个异常错误,仍然显示“JSON Error”消息,这样的错误也使代码更难调试。
幸运的是,我们能发现捕获的错误是那种类型,示例,从其name属性:
try { user = { /*...*/ }; } catch(e) { alert(e.name); // "ReferenceError" for accessing an undefined variable }
规则很简单。
应该仅处理已知错误,重新抛出其他错误
详细的重新抛出错误解释如下:
捕获所有错误
在
catch(err){...}块中,我们分析错误对象
err
如果不知道如何处理,那么通过
throw err抛出错误
下面的代码,我们使用重新抛出错误,这样
catch仅处理
SyntaxError:
let json = '{ "age": 30 }'; // incomplete data try { let user = JSON.parse(json); if (!user.name) { throw new SyntaxError("Incomplete data: no name"); } blabla(); // unexpected error alert( user.name ); } catch(e) { if (e.name == "SyntaxError") { alert( "JSON Error: " + e.message ); } else { throw e; // rethrow (*) } }
在
catch块内部星号行抛出错误,其可以被外部的
try...catch结构块捕获(如果存在),或直接停止脚本。
所以
catch块实际上仅处理已知错误,并忽略所有其他错误。
下面示例演示这样的错误被多级
try...catch块处理。
function readData() { let json = '{ "age": 30 }'; try { // ... blabla(); // error! } catch (e) { // ... if (e.name != 'SyntaxError') { throw e; // rethrow (don't know how to deal with it) } } } try { readData(); } catch (e) { alert( "External catch got: " + e ); // caught it! }
这里
readData仅知道如何处理SyntaxError错误,而外部的
try...catch知道如何处理任何错误。
try…catch…finally
等等,还没有完。
结构
try...catch可以有多个代码子句:
finally,如果存在,所有情况都会执行。
try之后,如果没有错误情况
catch之后,如果有错误
扩展语法类似如下:
try { ... try to execute the code ... } catch(e) { ... handle errors ... } finally { ... execute always ... }
请尝试运行下面代码:
try { alert( 'try' ); if (confirm('Make an error?')) BAD_CODE(); } catch (e) { alert( 'catch' ); } finally { alert( 'finally' ); }
代码有两条执行路径:
如果回答“Yes”产生一个错误,那么执行路径为
try->catch->finally.
如果回调“No”,那么路径为
try->finally.
子句
finally通常应用场景为:在
try...catch块之前开始做某事,无论结果如何都需要终止。
举例,我们想衡量斐波拉切函数
fib(n)执行时间,很自然,我们需要在执行前和结束后衡量。但如果在函数调用期间有错误?特别是,下面代码中的fib(n)的实现将返回一个针对负数或非整数的错误。
不管发生什么,子句
finally很适合去完成时间测量。
finally负责在两种场景下测试执行时间——成功执行
fib函数和错误情况:
let num = +prompt("Enter a positive integer number?", 35) let diff, result; function fib(n) { if (n < 0 || Math.trunc(n) != n) { throw new Error("Must not be negative, and also an integer."); } return n <= 1 ? n : fib(n - 1) + fib(n - 2); } let start = Date.now(); try { result = fib(num); } catch (e) { result = 0; } finally { diff = Date.now() - start; } alert(result || "error occured"); alert( `execution took ${diff}ms` );
你可以根据提示输入35,检查代码运行——执行正常,try之后执行finally。再次输入-1,立刻产生错误,执行花费0ms,正确完成时间测量。
换句话说,有两种方法可以退出函数:要么return,要么抛出错误。finally子句柄都会处理。
在try…catch…finally块中的变量是局部变量
注意在上面代码
result和
diff变量,是在
try...catch块之前声明的。
否则,如果使用let在{…}块里面,则只能在块内部可见。
finally 和 return
在
try...catch块无论如何结束,finally子句都执行。包括显示的return方式返回。
下面的示例,在try中有return,在这种情况,在控制返回外部代码之前,finally被执行。
function func() { try { return 1; } catch (e) { /* ... */ } finally { alert( 'finally' ); } } alert( func() ); // first works alert from finally, and then this one
try…finally
try…finally结构,没有
catch子句,也有用。当我们不想在这里处理错误时可以应用,但是要确保开始和最终过程被执行。
function func() { // start doing something that needs completion (like measurements) try { // ... } finally { // complete that thing even if all dies } }
在上面的代码中,try块的错误总会发生,因为没有catch块,但finally在执行流跳出外部之前会执行。
全局错误捕获
环境规范本节中的信息不是核心JavaScript的一部分。
我们想像在
try...catch块之外有个致命错误,那么代码会立刻停止。如编程错误或其他更糟糕的事情。
是否有应对此类事件的方法?我们可能想记录错误,向用户显示一些信息(通常他不会看到错误消息)等等。
Javascript规范中没有涉及,但环境通常都提供实现,因为确实有用。如,Node.JS有process.on(“uncaughtException”),在浏览器中可以给window.onerror赋值一个函数。它将在未捕获错误的情况下运行。
语法:
window.onerror = function(message, url, line, col, error) { // ... };
message
错误信息
url
发生错误脚本url
line, col
错误发生在代码中行、列数
error
错误对象
<script> window.onerror = function(message, url, line, col, error) { alert(`${message}\n At ${line}:${col} of ${url}`); }; function readData() { badFunc(); // Whoops, something went wrong! } readData(); </script>
全局错误处理
window.error的角色通常不能恢复脚本执行,在编程错误的情况下是不可能的,但会给开发者发送错误信息。
也有一些web服务为这种情况提供了错误日志记录,如 https://errorception.com 或 http://www.muscula.com.
工作流程如下:
注册服务,然后获得一段JS脚本,插入至页面中。
该JS脚本中有自定义的
window.error函数。
当错误发生时,会给服务器端发送网络请求。
我们可以登录服务的web界面查看错误信息。
总结
结构try...catch可以处理运行时错误,字面理解为尝试运行代码,然后捕获可能发生的错误。
语法为:
try { // run this code } catch(err) { // if an error happened, then jump here // err is the error object } finally { // do in any case after try/catch }
也可能没有
catch或
finally块,所以
try...catch和
try...finally都是有效语法。
错误对象有下面属性:
message——用户可以理解的错误信息。
name——错误名称字符串(错误构造函数名称)。
stack——非标准——发生错误是的堆栈信息。
我们也能通过使用
throw操作生成自己的错误,技术上,
throw的参数可以是任意类型,但通常是从内置错误类
Error继承的对象。后面会介绍扩展错误对象。
再次抛出是错误处理的基本模式:
catch块通常期望并知道怎样处理特定的错误,所以应该重新抛出未知错误。
即使我们没有使用
try...catch,大多数环境也支持设置全局的错误处理,捕获所有发生的错误,浏览器内置的是
window.onerror.
相关文章推荐
- JavaScript 错误处理, Throw、Try 和 Catch入门
- Javascript 错误处理:try throw catch
- 【JavaScript】错误处理:try......catch
- JavaScript错误_throw、try和catch
- SQL Server Try Catch错误处理
- PHP 的异常处理、错误处理:error_reporting,try-catch,trigger_error,set_error_handler,set_exception_handler,register_shutdown_function
- 从不用 try-catch 实现的 async/await 语法说错误处理
- 10 javaScript的异常处理 try{ }catch(theException){ }
- 从零开始学C++之异常(一):C语言错误处理方法、C++异常处理方法(throw, try, catch)简介
- JavaScript 错误 Throw、Try和Catch
- 错误处理try-catch,异常抛出throws、throw
- 从零开始学C++之异常(一):C语言错误处理方法、C++异常处理方法(throw, try, catch)简介
- IOS开发之--异常处理--使用try 和 catch 来捕获错误。
- JavaScript 错误 - Throw、Try 和 Catch
- JavaScript中try...catch和异常处理
- 001_JavaScript 错误 - Throw、Try 和 Catch
- SQL Server Try Catch错误处理
- 用Javascript的try{}catch(e){}也可以获得VBscript的错误
- javascript 之异常处理try catch finally--05
- JavaScript 例外处理Try{}catch(e){}