JavaScript 网页编程从入门到精通 (清华社"视频大讲堂"大系·网络开发视频大讲堂)
上QQ阅读APP看书,第一时间看更新

6.6 异常处理

异常处理语句包括throw、try、catch、finally,下面详细介绍。

6.6.1 异常概述

异常(Exception)是一个信号,提示代码发生了超出常规预设的行为或结果,异常也可能是程序发生错误的一种征兆。JavaScript有一套完善的异常处理机制,保证程序不因为异常而崩溃。

异常处理机制就是一套应对JavaScript代码发生错误时的处理方法,这套方法被封装在try/catch/finally结构中,把代码放在这个结构中执行就可以避免异常发生。

JavaScript把所有可能的错误分门别类地进行整理,并把它们封装在不同的对象中,这就是异常的种类。JavaScript内置的异常对象包括Error、EvalError、RangeError、SyntaxError、TypeError、ReferenceError和URIError。具体说明如表6-5所示。

表6-5 JavaScript内置异常对象

除了内置异常对象外,JavaScript允许用户使用自定义异常对象。

6.6.2 throw语句

throw语句能够主动抛出一个异常,告诉系统发生了异常状况或错误。throw语句的语法格式如下:

      throw expression;

expression可以是任意类型的表达式,一般常用它来声明Error对象或者Error子类的一个实例。

【示例】在下面循环结构中,定义了一个异常并使用throw语句把它抛出来,这样当循环变量大于5时,系统会自动弹出一个编译错误,提示“循环变量的值大于5了”的错误信息。

      for(var i = 0; i<10; i++){
          if(i>5)
            throw new Error("循环变量的值大于5了");  //定义并抛出一个异常
      }

在抛出异常时,JavaScript解释器会停止程序的正常执行,并跳转到与其最近的异常处理器(catch结构)。如果解释器没有找到异常处理器,则会检查上一级的catch结构,并依此类推,直到找到一个异常处理器为止。如果在程序中没有找到任何异常处理器,将会视其为错误并显示出来。

6.6.3 try/catch/finally语句

不管是系统抛出,还是用户有意抛出异常,都需要捕获(catch)异常,以便采取适当的动作把程序从异常状态恢复到正常运行状态。

try/catch/finally语句是JavaScript异常处理器,其中try从句负责指明需要处理的代码块。catch从句负责捕获异常,并决定应对之策。finally从句负责后期处理工作,如清除代码、释放资源等。不管异常是否发生,finally从句最后都是要执行的。整个异常处理的结构和从句之间的相互关系如下:

      try
      {
          //调试代码块
      }
      catch(e)
      {
          //捕获异常并进行处理
      }
      finally
      {
          //后期事务处理
      }

正常情况下,程序按顺序执行try从句中的代码,如果没有异常发生,将会忽略catch从句,跳转到finally从句中继续执行。如果在try从句中发生运行时错误或者使用throw语句主动抛出异常,则执行catch从句代码块,在该从句中通过参数变量引用抛出的Error对象或者其他值,同时定义处理异常的方法,或者忽略不计,或者再次抛出异常等。

注意:在异常处理结构中,大括号不是复合语句的一部分,而是异常处理结构的一部分,任何时候都不能够省略这些大括号。

【示例1】在下面的代码中,先在try从句中制造一个语法错误,即字符串没有加引号,然后在catch从句中利用参数变量b获取Error对象的引用,然后提示错误的详细信息,最后在finally从句中弹出正确的信息。

  
      try{                                                 //测试代码
          alert(a);                                        //制作语法错误
      }
      catch(b){                                            //捕获错误
          alert(b.message);                                //提示错误信息
      }
      finally{                                             //异常后期处理
        alert("a");                                        //提示正确值
      }

【示例2】在异常处理结构中,catch和finally从句是可选项目,可以根据需要省略,但在正常情况下必须包含try和catch从句。上面示例可以精简为:

      try{
          alert(a);
      }
      catch(b){}

finally从句比较特殊,不管try语句是否完全执行,finally语句最后都必须要执行,即使使用了跳转语句跳出了异常处理结构,也必须在跳出之前先执行finally从句。

如果没有catch从句,JavaScript在执行完try从句之后,继续执行finally从句,如果发生异常,会继续沿着语法结构链向上查找上一级catch从句。

try/catch语句可以相互嵌套,甚至可以在内层try/catch语句中又嵌套另一个内层try/catch语句,以及在该内层try/catch语句中再嵌套一个try/catch语句,嵌套的层数取决于实际代码的意义。

为什么需要使用嵌套的try/catch语句呢?因为使用嵌套的try/catch语句,可以逐步处理内层的try/catch语句抛出的异常。

【示例3】下面代码就是一个多层嵌套的异常结构,在处理一系列的异常时,内层的catch子句通过将异常抛出,就可以将异常抛给外层的catch子句来处理。

      try{                                                   //外层异常处理结构
          try{                                               //内层异常处理结构
              alret("Hi");                                   //错误引用方法
          }
          catch(exception){
              var e;
              if(! exception.description){                   //兼容非IE浏览器
                  e=exception.name;                          //获取错误名称
              }
              else{                                          //兼容IE浏览器
                  e=exception.description;                   //获取错误描述信息
              }
              if (e == "Object expected" || e == "ReferenceError"){
                                                             //如果是此类错误信息,则提示这样信息
                  alert("内层try/catch能够处理这个错误");
              } 
              else{                                          //否则再一次抛出一个异常
                  throw exception;
              }
          }
        }
        catch(exception){                                    //获取内层异常处理结构中抛出的异常
          alert("内层try/catch不能够处理这个错误");
        }

6.6.4 案例:改变作用域链

一般来说,一个运行期上下文的作用域链不会被改变,但是在JavaScript中,使用with或try/catch语句可以人为改变运行期上下文的作用域链。

当try子句块发生错误时,程序会自动转入catch子句块,并将异常对象推入到作用域链前端的一个可变的错误对象中。在catch子句块中,函数的所有局部变量现在被放在第二个作用域链对象中,例如:

      try {
            methodThatMightCauseAnError();
      } catch (ex) {
            alert(ex.message);
      }

注意:只要catch子句执行完毕,作用域链就会返回到原来的状态。

如果使用得当,try/catch结构将是非常有用的语句,可以通过精缩代码的办法最小化catch子句对性能的影响。一个很好的模式是将错误交给一个专用函数来处理,例如:

      try {
            methodThatMightCauseAnError();
      } catch (ex) {
            handleError(ex);
      }

handleError()函数是catch子句中运行的唯一代码。此函数以适当方法自由地处理错误,并接收由错误产生的异常对象。由于只有一条语句,没有局部变量访问,作用域链临时改变就不会影响代码的性能。