不管你多么精通编程,有时我们的脚本总还是会出现错误。可能是因为我们的编写出错,或是与预期不同的用户输入,或是错误的服务端响应以及其他数千种原因。

通常,如果发生错误,脚本就会“死亡”(立即停止),并在控制台将错误打印出来。

但是有一种语法结构 try..catch,它使我们可以“捕获(catch)”错误,因此脚本可以执行更合理的操作,而不是死掉。

“try…catch” 语法

try..catch 结构由两部分组成:trycatch

try {

  // 代码...

} catch (err) {

  // 错误捕获

}

它按照以下步骤执行:

  1. 首先,执行 try {...} 中的代码。
  2. 如果这里没有错误,则忽略 catch(err):执行到 try 的末尾并跳过 catch 继续执行。
  3. 如果这里出现错误,则 try 执行停止,控制流转向 catch(err) 的开头。变量 err(我们可以使用任何名称)将包含一个 error 对象,该对象包含了所发生事件的详细信息。

所以,try {…} 块内的错误不会杀死脚本 — 我们有机会在 catch 中处理它。

让我们来看一些例子。

  • 没有 error 的例子:显示 alert (1)(2)
try {

  alert('Start of try runs');  // (1) // ...这里没有 error  alert('End of try runs');   // (2) } catch(err) {  alert('Catch is ignored, because there are no errors'); // (3)}
  • 包含 error 的例子:显示 (1) 和 (3) 行的 alert 中的内容:
try {

  alert('Start of try runs');  // (1)   lalala; // Error,变量未定义!  alert('End of try (never reached)');  // (2)} catch(err) {  alert(`Error has occurred!`); // (3) }

try..catch 仅对运行时的 error 有效 要使得 try..catch 能工作,代码必须是可执行的。换句话说,它必须是有效的 JavaScript 代码。

如果代码包含语法错误,那么 try..catch 将无法正常工作,例如含有不匹配的花括号:

try {  {{{{{{{{{{{{} catch(e) {  alert("The engine can't understand this code, it's invalid");}

JavaScript 引擎首先会读取代码,然后运行它。在读取阶段发生的错误被称为“解析时间(parse-time)”错误,并且无法恢复(从该代码内部)。这是因为引擎无法理解该代码。

所以,try..catch 只能处理有效代码中出现的错误。这类错误被称为“运行时的错误(runtime errors)”,有时被称为“异常(exceptions)”。

try..catch 同步工作 如果在“计划的(scheduled)”代码中发生异常,例如在 setTimeout 中,则 try..catch 不会捕获到异常:

try {  setTimeout(function() {    noSuchVariable; // 脚本将在这里停止运行  }, 1000);} catch (e) {  alert( "won't work" );}

因为 try..catch 包裹了计划要执行的函数,该函数本身要稍后才执行,这时引擎已经离开了 try..catch 结构。

为了捕获到计划的(scheduled)函数中的异常,那么 try..catch 必须在这个函数内:

setTimeout(function() {  try {    noSuchVariable; // try..catch 处理 error 了!  } catch {    alert( "error is caught here!" );  }}, 1000);

Error 对象

发生错误时,JavaScript 生成一个包含有关其详细信息的对象。然后将该对象作为参数传递给 catch:

try {  // ...} catch(err) { // // ...}

对于所有内建的 error,error 对象具有两个主要属性:

name

Error 名称。例如,对于一个未定义的变量,名称是 "ReferenceError"。

message

关于 error 的详细文字描述。还有其他非标准的属性在大多数环境中可用。其中被最广泛使用和支持的是:

stack

当前的调用栈:用于调试目的的一个字符串,其中包含有关导致 error 的嵌套调用序列的信息。

例如:

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 (...call stack)

  // 也可以将一个 error 作为整体显示出来as a whole  // Error 信息被转换为像 "name: message" 这样的字符串  alert(err); // ReferenceError: lalala is not defined}

可选的 “catch” 绑定

如果我们不需要 error 的详细信息,catch 也可以忽略它:

try {  // ...} catch { // // ...}

使用 “try…catch”

让我们一起探究一下真实场景中 try..catch 的用例。

正如我们所知道的,JavaScript 支持 JSON.parse(str) 方法来解析 JSON 编码的值。

通常,它被用来解析从网络,从服务器或是从其他来源接收到的数据。

我们收到数据后,然后像下面这样调用 JSON.parse:

let json = '{"name":"John", "age": 30}'; // 来自服务器的数据

let user = JSON.parse(json); // 将文本表示转换成 JS 对象

// 现在 user 是一个解析自 json 字符串的有自己属性的对象alert( user.name ); // Johnalert( user.age );  // 30

如果 json 格式错误,JSON.parse 就会生成一个 error,因此脚本就会“死亡”。

我们对此满意吗?当然不!

如果这样做,当拿到的数据出了问题,那么访问者永远都不会知道原因(除非他们打开开发者控制台)。代码执行失败却没有提示信息,这真的是很糟糕的用户体验。

让我们用 try..catch 来处理这个 error:

let json = "{ bad json }";

try {

  let user = JSON.parse(json); //   alert( user.name ); // 不工作} catch (e) {// ...执行会跳转到这里并继续执行  alert( "Our apologies, the data has errors, we'll try to request it one more time." );  alert( e.name );  alert( e.message );}

在这儿,我们将 catch 块仅仅用于显示信息,但是我们可以做更多的事儿:发送一个新的网络请求,向访问者建议一个替代方案,将有关错误的信息发送给记录日志的设备,……。所有这些都比代码“死掉”好得多。

抛出我们自定义的 error

如果这个 json 在语法上是正确的,但是没有所必须的 name 属性该怎么办?

像这样:

let json = '{ "age": 30 }'; // 不完整的数据

try {

  let user = JSON.parse(json); //   alert( user.name ); // 没有 name!} catch (e) {  alert( "doesn't execute" );}

这里 JSON.parse 正常执行,但是缺少 name 属性对我们来说确实是个 error

为了统一进行 error 处理,我们将使用 throw 操作符。

“Throw” 操作符

throw 操作符会生成一个 error 对象。

语法如下:

throw

技术上讲,我们可以将任何东西用作 error 对象。甚至可以是一个原始类型数据,例如数字或字符串,但最好使用对象,最好使用具有 namemessage 属性的对象(某种程度上保持与内建 error 的兼容性)。

JavaScript 中有很多内建的标准 error 的构造器:ErrorSyntaxErrorReferenceErrorTypeError 等。我们也可以使用它们来创建 error 对象。

它们的语法是:

let error = new Error(message);// 或let error = new SyntaxError(message);let error = new ReferenceError(message);// ...

对于内建的 error(不是对于其他任何对象,仅仅是对于 error),name 属性刚好就是构造器的名字。message 则来自于参数(argument)。

例如:

let error = new Error("Things happen o_O");

alert(error.name); // Erroralert(error.message); // Things happen o_O

让我们来看看 JSON.parse 会生成什么样的 error

try {  JSON.parse("{ bad json o_O }");} catch(e) {  alert(e.name); // SyntaxError  alert(e.message); // Unexpected token b in JSON at position 2}

正如我们所看到的, 那是一个 SyntaxError

在我们的示例中,缺少 name 属性就是一个 error,因为用户必须有一个 name

所以,让我们抛出这个 error

let json = '{ "age": 30 }'; // 不完整的数据

try {

  let user = JSON.parse(json); // 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 操作符生成了包含着我们所给定的 messageSyntaxError,与 JavaScript 自己生成的方式相同。try 的执行立即停止,控制流转向 catch 块。

现在,catch 成为了所有 error 处理的唯一场所:对于 JSON.parse 和其他情况都适用。

try…catch…finally

等一下,以上并不是所有内容。

try..catch 结构可能还有一个代码子句(clause):finally

如果它存在,它在所有情况下都会被执行:

  • try 之后,如果没有 error,
  • catch 之后,如果没有 error。

该扩展语法如下所示:

try {   ... 尝试执行的代码 ...} catch(e) {   ... 处理 error ...} finally {   ... 总是会执行的代码 ...}

试试运行这段代码:

try {  alert( 'try' );  if (confirm('Make an error?')) BAD_CODE();} catch (e) {  alert( 'catch' );} finally {  alert( 'finally' );}

这段代码有两种执行方式:

如果你对于 “Make an error?” 的回答是 “Yes”,那么执行 try -> catch -> finally。如果你的回答是 “No”,那么执行 try -> finallyfinally 子句(clause)通常用在:当我们开始做某事的时候,希望无论出现什么情况都要完成完成某个任务。

例如,我们想要测量一个斐波那契数字函数 fib(n) 执行所需要花费的时间。通常,我们可以在运行它之前开始测量,并在运行完成时结束测量。但是,如果在该函数调用期间出现 error 该怎么办?特别是,下面这段 fib(n) 的实现代码在遇到负数或非整数数字时会返回一个 error

无论如何,finally 子句都是一个结束测量的好地方。

在这儿,finally 能够保证在两种情况下都能正确地测量时间 — 成功执行 fib 以及 fib 中出现 error 时:

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 occurred");

alert( `execution took ${diff}ms` );

你可以通过运行上面这段代码并在 prompt 弹窗中输入 35 来进行检查 — 代码运行正常,先执行 try 然后是 finally。如果你输入的是 -1 — 将立即出现 error,执行将只花费 0ms。以上两种情况下的时间测量都正确地完成了。

换句话说,函数 fib 以 return 还是 throw 完成都无关紧要。在这两种情况下都会执行 finally 子句。

变量和 try..catch..finally 中的局部变量 请注意,上面代码中的 result 和 diff 变量都是在 try..catch 之前 声明的。否则,如果我们使用 let 在 try 块中声明变量,那么该变量将只在 try 块中可见。

finally 和 return finally 子句适用于 try..catch 的 任何 出口。这包括显式的 return。在下面这个例子中,在 try 中有一个 return。在这种情况下,finally 会在控制转向外部代码前被执行。

function func() {

  try {    return 1;

  } catch (e) {    /* ... */  } finally {    alert( 'finally' );  }}

alert( func() ); // 先执行 finally 中的 alert,然后执行这个 alert

try..finally没有 catch 子句的 try..finally 结构也很有用。当我们不想在这儿处理 error(让它们 fall through),但是需要确保我们启动的处理需要被完成。

function func() {  // 开始执行需要被完成的操作(比如测量)  try {    // ...  } finally {    // 完成前面我们需要完成的那件事儿,即使 try 中的执行失败了  }}

上面的代码中,由于没有 catch,所以 try 中的 error 总是会使代码执行跳转至函数 func() 外。但是,在跳出之前需要执行 finally 中的代码。

全局 catch

设想一下,在 try..catch 结构外有一个致命的 error,然后脚本死亡了。这个 error 就像编程错误或其他可怕的事儿那样。

有什么办法可以用来应对这种情况吗?我们可能想要记录这个 error,并向用户显示某些内容(通常用户看不到错误信息)等。

规范中没有相关内容,但是代码的执行环境一般会提供这种机制,因为它确实很有用。例如,Node.JS 有 process.on("uncaughtException")。在浏览器中,我们可以将将一个函数赋值给特殊的 window.onerror 属性,该函数将在发生未捕获的 error 时执行。

语法如下:

window.onerror = function(message, url, line, col, error) {  // ...};

message

Error 信息。

url

发生 error 的脚本的 URL。

line,col

发生 error 处的代码的行号和列号。

error

Error 对象。

控制台怎么查看错误的详细信息_js错误处理,quot;try..catchquot;相关推荐

  1. 服务器上出现应用程序错误。此应用程序的当前自定义错误设置禁止远程查看应用程序错误的详细信息(出于安全原因)。未能加载文件或程序集“XXXXXX”或它的某一个依赖项。拒绝访问。

    前景:服务器在本地的VS编辑器运行起来正常,但是部署上就会报错. 我开发的这个项目,是因为后台要对客户端前台上传的Excel表进行操作,所以才会出现以下问题,在这之前, IIS服务器会要求把项目网站对 ...

  2. 如何查看电脑显卡详细信息以及nvidia-smi各参数介绍

    主要分为两个部分: 如何查看电脑显卡详细信息 nvidia-smi各参数介绍 1. 如何查看电脑显卡详细信息 不论是windows系统还是linux,打开命令行,输入 nvidia-smi 即可. w ...

  3. linux查看镜像的详细信息,docker inspect命令查看镜像详细信息

    # docker inspect ubuntu:18.04 // 查看镜像的详细信息[ {"Id": "sha256:d131e0fa2585a7efbfb187f70d ...

  4. 异常信息:由于内部错误,服务器无法处理该请求。有关该错误的详细信息,请打开服务器上的 IncludeExceptionDetailInFaults

    异常信息:由于内部错误,服务器无法处理该请求.有关该错误的详细信息,请打开服务器上的 IncludeExceptionDetailInFaults <behaviors><endpo ...

  5. 点石互动--石头之:在Google中查看链接的详细信息

    点石互动   highdiy 发表于 03月 14, 2007 在Google网站管理员中心提供详细的反相链接信息后,使得以前许多不得不注册个Yahoo! SiteExplorer帐号以查询网站反相链 ...

  6. Chemex3.4 怎么修改资产设备那原始二维码,扫码可查看该资产详细信息

    环境: docker Version: 18.06.3-ce mysql 5.7 chemex 3.4 vmware 16.0 问题描述: chemex系统资产设备那原始二维码,显示的是资产编号的二维 ...

  7. linux下查看运行进程详细信息

    通过ps及top命令查看进程信息时,只能查到相对路径,查不到的进程的详细信息,如绝对路径等.这时,我们需要通过以下的方法来查看进程的详细信息: Linux在启动一个进程时,系统会在/proc下创建一个 ...

  8. [.crt 证书] openssl 查看.crt 证书详细信息

    openssl 查看.crt 证书详细信息 // 查看证书信息 openssl x509 -in /Users/xiaxveliang/Downloads/getssl.crt -noout -tex ...

  9. 控制台怎么查看错误的详细信息_Java 程序该怎么优化?命令篇

    灵魂拷问,JDK 提供的命令,除了 java.javac,你还用过哪些命令呢? 灵魂再拷问,若你写的 Java 程序,出现了性能问题,该怎么去排查呢? Java 作为编程语言中的战斗机,JDK 默认已 ...

最新文章

  1. 在Struts2中使用OGNL
  2. 几率大的REDIS面试题
  3. Java程序员面试中的多线程问题1
  4. json里面返回模板输出的html,在JSON响应中返回完整页面的HTML而不是字符串输出...
  5. 论软件的模块化与架构
  6. python教程:实现延时回调普通函数的方法
  7. Android studio 克隆分支
  8. java中ftp删除文件,Java 实现ftp 文件上传、下载和删除
  9. 再也不学AJAX了!(二)使用AJAX
  10. 高动态范围图像HDR
  11. table T371A field IBTYP的search help
  12. 【spring cloud】注解@SpringCloudApplication和@SpringBootApplication的区别
  13. [数据库笔记]规范化(Normalization)-把数据冗余降下来
  14. 机器学习笔记网盘分享
  15. 重磅:阿里发布神器工具,直接帮你改代码,我高潮了!网友:工作量又减轻了!...
  16. 如何在Arduino上使用OV7670摄像头模块
  17. 5G聚合路由器有哪些优势?能应用在哪些场景?
  18. 孤独求败:美国海军邀黑客“黑掉军舰”
  19. ZigBee无线通信--BasicRF无线点对点通信
  20. Usb rndis,mtp等function添加,config配置

热门文章

  1. eclipse 使用lombok 出现的问题
  2. ejb 属于哪一层,作用是什么,什么时候用
  3. 高效java工具类总结
  4. SpringBoot2.0之一 新建项目helloWorld
  5. jquery获取前一个月日期
  6. (转)Hibernate关联映射——一对多(多对一)
  7. Python 数据科学手册 5.1 什么是机器学习
  8. 使用pip安装opencv
  9. 基于 Vue BootStrap的迷你Chrome插件
  10. MySQL(26)--- 索引