• 【转】Javascript错误处理——try…catch


    无论我们编程多么精通,脚本错误怎是难免。可能是我们的错误造成,或异常输入,错误的服务器端响应以及无数个其他原因。

    通常,当发送错误时脚本会立刻停止,打印至控制台。

    try...catch语法结构可以捕获错误并可以做更多的事情,而不是直接停止。

    因此,推荐使用Fundebug做线上监控,第一时间发现报错。  

    “try…catch” 语法

    try...catch结构有两个语句块,即try,然后catch

    try {
    
      // code...
    
    } catch (err) {
    
      // error handling
    
    }
    

    工作流程如下:

    1. 首先try{...}代码块执行。
    2. 如果没有错误,那么catch(err)被忽略:执行到try结尾时,跳过catch块。
    3. 如果发生错误,那么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
    }

    规则很简单。

    应该仅处理已知错误,重新抛出其他错误 
    详细的重新抛出错误解释如下:

    1. 捕获所有错误
    2. catch(err){...}块中,我们分析错误对象err
    3. 如果不知道如何处理,那么通过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' );
    }
    

    代码有两条执行路径:

    1. 如果回答“Yes”产生一个错误,那么执行路径为try->catch->finally.
    2. 如果回调“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块中的变量是局部变量

    注意在上面代码resultdiff变量,是在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}
     At ${line}:${col} of ${url}`);
      };
    
      function readData() {
        badFunc(); // Whoops, something went wrong!
      }
    
      readData();
    </script>

    全局错误处理window.error的角色通常不能恢复脚本执行,在编程错误的情况下是不可能的,但会给开发者发送错误信息。

    也有一些web服务为这种情况提供了错误日志记录,如 https://errorception.com 或 http://www.muscula.com.

    工作流程如下:

    1. 注册服务,然后获得一段JS脚本,插入至页面中。
    2. 该JS脚本中有自定义的window.error函数。
    3. 当错误发生时,会给服务器端发送网络请求。
    4. 我们可以登录服务的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
    }

    也可能没有catchfinally块,所以try...catchtry...finally都是有效语法。

    错误对象有下面属性:

    • message——用户可以理解的错误信息。
    • name——错误名称字符串(错误构造函数名称)。
    • stack——非标准——发生错误是的堆栈信息。

    我们也能通过使用throw操作生成自己的错误,技术上,throw的参数可以是任意类型,但通常是从内置错误类Error继承的对象。后面会介绍扩展错误对象。

    再次抛出是错误处理的基本模式:catch块通常期望并知道怎样处理特定的错误,所以应该重新抛出未知错误。

    即使我们没有使用try...catch,大多数环境也支持设置全局的错误处理,捕获所有发生的错误,浏览器内置的是window.onerror.

    原文:https://blog.csdn.net/neweastsun/article/details/76358623

    您可能感兴趣的

    1. 详解1000+项目数据分析出来的10大JavaScript错误
    2. 提示“用户名或密码不正确”很糟糕
    3. Debug前端HTML/CSS
    4. 有浏览器的地方就有Fundebug
  • 相关阅读:
    大三上学期周总结
    《代码整洁之道》阅读笔记(三)
    大三上学期周总结
    【测试技能】常用adb命令记录
    【Jmeter】Mac本JMeter实现压力测试实例
    【音视频】IP 地区定位,有坑
    【服务器】mp(edge)内存使用情况扩展
    【python】TypeError: 'Response' object is not subscriptable
    【python】单元测试框架改造接口自动化case
    【git】放弃本地修改,拉取远端最新代码
  • 原文地址:https://www.cnblogs.com/curationFE/p/js_try_catch.html
Copyright © 2020-2023  润新知