JavaScript——异常处理

发布时间:2026/7/22 14:56:11

JavaScript——异常处理 异常处理1、捕获异常2、throw抛出异常3、Error对象4、自定义异常5、finally6、捕获多个异常1、捕获异常在编写代码时经常在控制台看到TypeError、RangeError、ReferenceError等开头的红色错误信息这些都是程序发出的警告告知开发者出现了什么问题并附加与代码有关的信息例如出错的位置。这时可以在代码出错的地方使用try…catch语句块对异常进行捕获和处理。try{}语句块用于监控内部的代码catch{}语句块用于捕获到异常后编写异常处理语句。假设如果一个值应该为对象的变量而实际上其值为undefined则调用它的方法就会抛出TypeError异常代码如下letobjundefined;obj.method();//Uncaught TypeError:Cannot read property methodof undefined//未捕获的类型异常:不能访问undefined的method属性console.log这行代码不会被执行;在异常抛出后它后边的代码便不会被执行了如果obj.method()后边还有其他代码则它们永远不会被执行使用try…catch把异常捕获之后就能解决这个问题了代码如下try{letobjundefined;obj.method();}catch(e){console.error(e.name);console.error不能访问undefined中的方法;}console.log此行能正常执行;TypeError 不能访问undefined中的方法 此行能正常执行catch语句块中的小括号接收一个可选的参数如果不接收则不写小括号​是try中代码抛出来的Error对象稍后再介绍有关Error的内容这里访问了它的name属性打印出了具体的Error对象名字后面打印了自定义的异常信息提示不能访问undefined中的方法。这样异常就被捕获住了并且后边的代码还会正常执行。在catch语句块打印日志时可以使用console.error()以让控制台的显示格式为错误信息。如果只想捕获异常但不处理则也可以省略catch语句块的参数和其中的内容代码如下try{letobjundefined;obj.method();}catch{}2、throw抛出异常有时候可能明确知道需要在什么地方抛出异常而不是仅靠JavaScript运行时自动判断例如判断函数参数是否符合规则如果不符合就抛出异常这时可以通过throw关键字自行抛出异常。如果后续使用catch语句块捕获了该异常则throw后边的表达式会传递给catch的参数如果没有捕获异常则会直接将异常打印到控制台。另外throw后边的代码不会被执行。throw关键字用法的代码如下functionsetName(name){if(!name)throwname不能为空;console.log这行代码不会被执行;}//未捕获异常setName();//name不能为空捕获异常try{setName();}catch(e){console.error(e);//name不能为空}它会打印出name不能为空e参数的值就是throw后边抛出的字符串。throw关键字后边可以是任何类型的值或表达式例如下方的throw语句都是合法的throw10;//抛出数字throwfalse;//抛出布尔值throw[];//抛出数组throw{};//抛出对象throw语句几乎可以用在任何地方。可以在全局代码中、普通函数中、构造函数中、对象或类的方法中但是不能用在需要表达式的地方例如数组元素、函数参数、对象属性值等当中。如果throw语句嵌套的层次比较深则可以在任何一层进行异常处理例如当编写后端Web应用程序时一般会在处理请求和响应的控制器中统一处理用户输入错误、数据库错误和业务逻辑错误等这样下层的代码可以直接抛出异常然后在最上层统一进行处理代码如下functionmapArr(arr){arr.map((v){if(v2){throwerror;}console.log(v);});}try{mapArr([1,2,3]);//1 error}catch(error){console.log(error);}mapArr使用数组的map()方法遍历参数数组在map()的回调函数中判断如果遍历到的值为2就抛出异常其他值则直接打印出来。代码的输出结果为1error这是因为throw语句后边的代码不会被执行所以在遍历数组元素2时map()回调函数就停止执行了。这时throw语句在map()的回调函数中后边可以在调用mapArr()的时候再去捕获异常就像上方代码一般。另外也可以在mapArr()函数中捕获异常代码如下functionmapArr(arr){try{arr.map((v){if(v2){throwerror;}console.log(v);});}catch(error){console.log(error);}}具体在何时处理异常就要看具体的业务需求和对代码的影响。如果在一段代码中使用了可能抛出异常的代码并且想做一些处理操作之后把相同的异常再次抛出则可以直接在catch{}语句块中使用throw语句把catch的参数抛出。假设有一系列处理用户请求的函数在收到查询某个特定用户信息的请求之后由控制器交给业务逻辑层处理再由业务逻辑层去查询数据库当数据库出错时在业务逻辑层做一些处理操作例如记录日志​然后直接把异常抛给控制器处理这时就可以使用再抛出(Rethrow)来把异常原样交给上层去处理代码如下functionqueryDb(id){throw未在数据库中找到该条记录;}functiongetUserByIdService(id){try{queryDb(id);}catch(error){//一些其他操作throwerror;}}functiongetUserController(id){try{getUserByIdService(id);}catch(error){console.log(error);}}getUserController(1);在getUserByIdService()函数中当在catch里处理完其他操作之后使用了throw把error参数抛出之后在getUserController()中处理了这个异常。throw后边也可以抛出更具有实际意义的Error对象11.3节来看一下它的用法。3、Error对象Error是JavaScript内置的对象用于表示异常信息它有name和message两个属性分别为异常的名字和消息它还有一个toString()方法用于把异常信息转换为字符串。这些属性和方法是浏览器和Node.js都支持的不过对于不同的浏览器Error还会有更多的属性例如lineNumber异常出现的行号、columnNumber异常出现的列号和stack异常的堆栈信息。不过Error对象是基础对象一般需要通过继承的方式来定义更明确的异常对象。JavaScript根据Error对象扩展出了如下几种内置的异常对象。TypeError表示变量或参数不是正确的类型例如访问undefined中的方法。RangeError表示数字类型的变量或参数超出了指定范围或无效。例如new Array(NaN)。ReferenceError在引用不存在的变量时抛出例如console.log(a)。SyntaxError在使用错误的语法时抛出。例如let 325使用了数字作为变量名​。这些异常的name属性值就是各自的构造函数的名字如TypeError、RangeError。在使用throw语句时可以在它后边的表达式中直接创建Error对象或创建上述内置的其他异常对象这样可以让异常包含更丰富的信息以便于开发者根据提示处理异常。要创建这些异常对象可以直接使用它们的构造函数并传递一个message参数表示错误消息代码如下functiondivision(a,b){if(b0)thrownewError除数不能为0;returna/b;}division(5,0);//Error:除数不能为0//at division(anonymous:2:21)//atanonymous:1:1示例中的division()函数用于除法计算如果除数为0就抛出异常。当后边给参数b传递值0时该异常就会抛出控制台会打印出Error除数不能为0的异常消息和堆栈信息。异常消息是通过调用Error中的toString()转换成的字符串其中冒号前边为Error对象中name属性的值即异常的名字后边是message属性的值。上述throw语句也可以抛出一个更明确的RangeError:throw newRangeError除数不能为0​这样异常对象的name属性就会变为RangeError打印出来的信息会变为RangeError除数不能为0。4、自定义异常大部分情况下JavaScript内置的异常对象不能满足业务的要求一般的程序中都应该定义自己的异常对象来生成更具有业务意义上的异常对象例如RESTful API请求异常、数据验证异常等。通过继承Error对象然后修改其中默认的错误消息和处理方式并添加自定义的异常信息和业务逻辑就可以快速创建自定义的异常对象。假设程序在接收到用户输入之后需要判断数据是否符合验证规则如果不符合则抛出异常那么这个异常可以定义为 ValidationError它除了包括既有的name和message属性之外还包括用户原始输入信息要创建它可以通过原型方式或class方式由于class方式比较直观所以本示例使用class实现继承代码如下classValidationErrorextendsError{constructor(message,input){supermessage,用户输入:input;this.nameValidationError.name;this.inputinput;}}functionvalidatePassword(pwd){if(!pwd||pwd.length8)thrownewValidationError密码不能小于8位,pwd;returntrue;}try{validatePassword(123456);}catch(e){console.log(einstanceofValidationError);console.log(e.name);console.log(e.message);console.log(e.input);}ValidationError继承了Error对象在构造函数中首先调用父类Error的构造函数使用supermessage用户输入input​初始化了message属性并在message属性值的基础上加上了用户输入的值。后边给ValidationError类新添加了一个input属性用于保存用户原始输入信息name属性则设置为当前类的name属性值即ValidationError。validatePassword()函数用于简单地判断密码是否大于或等于8位如果不是则抛出ValidationError并分别设置异常消息和用户原始输入。在调用这种方法时使用try…catch捕获了这个异常并打印了异常的信息判断它是不是ValidationError类的实例、异常的名字、消息和用户输入。上述代码的输出结果如下trueValidationError 密码不能小于8位,用户输入:123456123456在日常开发中应尽量使用自定义的异常这样能够使异常信息更具体更符合实际的业务逻辑。5、finally在try…catch语句块中还可以添加可选的finally语句块一般用于在抛出异常后执行一些收尾和清理操作例如关闭数据库或文件访问句柄。finally语句块的代码里边的语句必定会被执行无论try或catch是否执行。如果有catch语句块则finally语句块会在catch语句块之后被执行如果没有catch语句块则会在try语句块之后被执行。finally语句用法的代码如下try{console.log获取数据库连接对象;throw出现错误}catch{console.log不能获取连接;}finally{console.log关闭数据库对象}上述代码会输出的结果如下获取数据库连接对象 不能获取连接 关闭数据库对象可以看到try、catch和finally的语句块都按顺序执行了这时如果把try中的throw注释掉则catch中的不能获取连接就不会打印但是finally中的关闭数据库对象仍然会打印。不过需要注意的是如果try中有return语句则finally语句仍然会被执行且在return语句后被执行例如把上方代码稍做改动放到函数中并添加一个conn变量和return语句来测试一下finally和return的执行顺序代码如下functiongetConnection(){letconnnull;try{console.log获取数据库连接对象;conn连接对象;returnconn;}catch{console.log不能获取连接;}finally{console.log关闭数据库对象;conn连接已关闭;}}letconngetConnection();console.log(conn);//连接对象可以看到conn先于return被执行出来了console.log(conn)打印出了finally对conn进行修改前的值为了进一步验证可以在finally再加上一个return语句代码如下finally{console.log关闭数据库对象;conn连接已关闭;returnconn;}这次打印出来了连接已关闭因为finally语句块最后被执行所以它里边的return覆盖掉了try中的return语句。当然这里只是为了演示它们的顺序在实际开发中不应该在同一函数中有两个return语句。在捕获异常时catch和finally语句块至少需要存在一个要么是try…catch要么是try…finally要么是try…catch…finally如果只有一个try{}语句则程序会抛出语法错误提示try后边缺少catch或finally语句代码如下try{throw异常}//语法错误:try后边缺少catch或finally语句6、捕获多个异常有些程序代码可能会同时抛出多个异常在常见的后端开发中数据库的访问、业务逻辑的处理、用户请求的处理等都有可能抛出异常然后在控制器最上层处理用户请求的地方中统一进行异常处理这时需要根据不同的异常类型来响应不同的HTTP状态码。不过JavaScript不像Java中可以用多个catch语句块捕获多种不同类型的异常它只支持一个catch语句块在里边可以通过if/else语句进行异常判断。例如下方示例在一个函数中返回了不同类型的异常并且在catch语句块中同时处理代码如下functiondivision(a,b){if(typeofa!number)thrownewTypeErrora必须为数字;if(typeofb!number)thrownewTypeErrorb必须为数字;if(b0)thrownewRangeError除数不能为0;returna/b;}try{division(1,a);//division(1,0);}catch(e){if(einstanceofTypeError){console.log类型不正确;}elseif(einstanceofRangeError){console.log取值不正确;}else{console.log(e);}}上述代码会输出类型不正确如果把division(1a)注释掉并取消division(1,0)的注释则会打印出取值不正确。

相关新闻