JavaScript中的错误处理机制

在写代码的时候,避免不了遇到各种各样的错误,遇到错误,BUG,我们需要尽快的解决,才能不拖累工作的进度,我们一般都会百度错误如何解决,但是遇到一些针对性错误以及百度方法不管用的情况下,就需要了解错误处理机制了。

Error对象

JavaScript解析或执行时,一旦发生错误,引擎就会抛出一个错误对象。JavaScript原生提供一个Error构造函数,所有抛出的错误都是这个构造函数的实例。

var err = new Error('出错了');
err.message // "出错了"

上面代码中,我们调用Error构造函数,生成一个err实例。

Error构造函数接受一个参数,表示错误提示,可以从实例的message属性读到这个参数。

代码解析或运行时发生错误,JavaScript引擎就会自动产生、并抛出一个Error对象的实例,然后整个程序就中断在发生错误的地方,不再往下执行。

根据语言标准,Error对象的实例必须有message属性,表示出错时的提示信息,其他属性则没有提及。大多数JavaScript引擎,对Error实例还提供name和stack属性,分别表示错误的名称和错误的堆栈,但它们是非标准的,不是每种实现都有。

属性:

1.message:错误提示信息
2.name:错误名称(非标准属性)
3.stack:错误的堆栈(非标准属性)

利用name和message这两个属性,可以对发生什么错误有一个大概的了解。

if (error.name){
  console.log(error.name + ": " + error.message);
}

上面代码表示,显示错误的名称以及出错提示信息。

stack属性用来查看错误发生时的堆栈。

function throwit() {
  throw new Error('');
}
 
function catchit() {
  try {
    throwit();
  } catch(e) {
    console.log(e.stack); // print stack trace
  }
}
 
catchit()
// Error
//    at throwit (~/examples/throwcatch.js:9:11)
//    at catchit (~/examples/throwcatch.js:3:9)
//    at repl:1:5

上面代码显示,抛出错误首先是在throwit函数,然后是在catchit函数,最后是在函数的运行环境中。

JavaScript的原生错误类型

Error对象是最一般的错误类型,在它的基础上,JavaScript还定义了其他6种错误,也就是说,存在Error的6个派生对象。

(1)SyntaxError

SyntaxError是解析代码时发生的语法错误。

// 变量名错误
var 1a;
 
// 缺少括号
console.log 'hello');

(2)ReferenceError

ReferenceError是引用一个不存在的变量时发生的错误。

unknownVariable
// ReferenceError: unknownVariable is not defined

另一种触发场景是,将一个值分配给无法分配的对象,比如对函数的运行结果或者this赋值。

console.log() = 1
// ReferenceError: Invalid left-hand side in assignment
 
this = 1
// ReferenceError: Invalid left-hand side in assignment

上面代码对函数console.log的运行结果和this赋值,结果都引发了ReferenceError错误。

(3)RangeError

RangeError是当一个值超出有效范围时发生的错误。主要有几种情况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。

new Array(-1)
// RangeError: Invalid array length
 
(1234).toExponential(21)
// RangeError: toExponential() argument must be between 0 and 20 

(4)TypeError

TypeError是变量或参数不是预期类型时发生的错误。比如,对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,因为new命令的参数应该是一个构造函数。

new 123
//TypeError: number is not a func
 
var obj = {};
obj.unknownMethod()
// TypeError: obj.unknownMethod is not a function 

上面代码的第二种情况,调用对象不存在的方法,会抛出TypeError错误。

(5)URIError

URIError是URI相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数。

decodeURI('%2')
// URIError: URI malformed

(6)EvalError

eval函数没有被正确执行时,会抛出EvalError错误。该错误类型已经不再在ES5中出现了,只是为了保证与以前代码兼容,才继续保留。

以上这6种派生错误,连同原始的Error对象,都是构造函数。开发者可以使用它们,人为生成错误对象的实例。

try catch finally语句与捕获错误

ECMA-262第3版引入了try-catch语句,作为javascript中处理异常的一种标准方式,用于捕获和处理错误。

其中,try从句定义了需要处理的异常所在的代码块。catch从句跟随在try从句之后,当try块内某处发生了异常时,调用catch内的代码逻辑。catch从句后跟随finally块,后者中放置清理代码,不管try块中是否产生异常,finally块内的逻辑总是会执行。尽管catch和finally都是可选的,但try从句需要至少二者之一与之组成完整的语句

try/catch/finally语句块都需要使用花括号括起来,这里的花括号是必需的,即使从句中只有一条语句也不能省略花括号。

try{
    //通常来讲,这里的代码会从头到尾而不会产生任何问题
    //但有时会抛出一个异常,要么是由throw语句直接抛出,要么通过调用一个方法间接抛出
}catch(e){
    //当且仅当try语句块抛出了异常,才会执行这里的代码
    //这里可以通过局部变量e来获得对Error对象或者抛出的其他值的引用
    //这里的代码块可以基于某种原因处理这个异常,也可以忽略这个异常,还可以通过throw语句重新抛出异常
}finally{
    //不管try语句是否抛出了异常,finally里的逻辑总是会执行,终止try语句块的方式有:
    //1、正常终止,执行完语句块的最后一条语句
    //2、通过break、continue或return语句终止
    //3、抛出一个异常,异常被catch从句捕获
    //4、抛出一个异常,异常未被捕获,继续向上传播
}

一般地,把所有可能会抛出错误的代码都放在try语句块中,而把那些用于错误处理的代码放在catch块中。

如果try块中的任何代码发生了错误,就会立即退出代码执行过程,然后接着执行catch块。此时,catch块会接收到一个错误信息的对象,这个对象中包含的实际信息会因浏览器而异,但共同的是有一个保存着错误消息的message属性

[注意]一定要给error对象起个名字,置空会报语法错误

try{
    q;
}catch(error){
    alert(error.message);//q is not defined
}

//Uncaught SyntaxError: Unexpected token )
try{
    q;
}catch(){
    alert(error.message);
}

catch接受一个参数,表示try代码块抛出的值。

function throwIt(exception) {
  try {
    throw exception;
  } catch (e) {
    console.log('Caught: '+ e);
  }
}

throwIt(3);// Caught: 3
throwIt('hello');// Caught: hello
throwIt(new Error('An error happened'));// Caught: Error: An error happened

catch代码块捕获错误之后,程序不会中断,会按照正常流程继续执行下去。

try{
  throw "出错了";
} catch (e) {
  console.log(111);
}
console.log(222);
// 111
// 222

为了捕捉不同类型的错误,catch代码块之中可以加入判断语句

try {
  foo.bar();
} catch (e) {
  if (e instanceof EvalError) {
    console.log(e.name + ": " + e.message);
  } else if (e instanceof RangeError) {
    console.log(e.name + ": " + e.message);
  }
  // ...
}

虽然finally子句在try-catch语句中是可选的,但finally子句一经使用,其代码无论如何都会执行。换句话说,try语句块中的代码全部正常执行,finally子句会执行;如果因为出错而执行了catch语句块,finally子句照样还会执行。只要代码中包含finally子句,则无论try或catch语句块中包含什么代码——甚至return语句,都不会阻止finally子句的执行

function f() {
  try {
    console.log(0);
    throw "bug";
  } catch(e) {
    console.log(1);
    return true; // 这句原本会延迟到finally代码块结束再执行
    console.log(2); // 不会运行
  } finally {
    console.log(3);
    return false; // 这句会覆盖掉前面那句return
    console.log(4); // 不会运行
  }
  console.log(5); // 不会运行
}
var result = f();
// 0
// 1
// 3

console.log(result);// false

[注意]return语句的count的值,是在finally代码块运行之前,就获取完成了。

throw语句与抛出错误

throw语句用于抛出错误。抛出错误时,必须要给throw语句指定一个值,这个值是什么类型,没有要求。

[注意]抛出错误的过程是阻塞的,后续代码将不会执行

throw 12345;
throw 'hello world';
throw true;
throw {name: 'javascript'};

可以使用throw语句手动抛出一个Error对象

throw new Error('something bad happened');

throw new SyntaxError('I don\'t like your syntax.');
throw new TypeError('what type of variable do you take me for?');
throw new RangeError('sorry,you just don\'t have the range.');
throw new EvalError('That doesn\'t evaluate.');
throw new URIError('URI, is that you?');
throw new ReferenceError('you didn\'t cite your references properly');

利用原型链还可以通过继承Error来创建自定义错误类型。此时,需要为新创建的错误类型指定name和message属性。

可以通过自定义一个assert函数,规范化throw抛出的信息。

function assert(expression, message) {
  if (!expression)
    throw {name: 'Assertion Exception', message: message};
}

上面代码定义了一个assert函数,它接受一个表达式和一个字符串作为参数。一旦表达式不为真,就抛出指定的字符串。它的用法如下。

assert(typeof myVar != 'undefined', 'myVar is undefined!');

浏览器对待继承自Error的自定义错误类型,就像对待其他错误类型一样。如果要捕获自己抛出的错误并且把它与浏览器错误区别对待的话,创建自定义错误是很有用的

function CustomError(message){
    this.name = 'CustomError';
    this.message = message;
}
CustomError.prototype = new Error();
throw new CustomError('my message');

在遇到throw语句时,代码会立即停止执行。仅当有try-catch语句捕获到被抛出的值时,代码才会继续执行。

更详细的解释为:当抛出异常时,javascript解释器会立即停止当前正在执行的逻辑,并跳转到就近的异常处理程序。异常处理程序是用try-catch语句的catch从句编写的。如果抛出异常的代码块没有一条相关联的catch从句,解释器会检查更高层的闭合代码块,看它是否有相关联的异常处理程序。以此类推,直到找到一个异常处理程序为止。如果抛出异常的函数没有处理它的try-catch语句,异常将向上传播到调用该函数的代码。这样的话,异常就会沿着javascript方法的词法结构和调用栈向上传播。如果没有找到任何异常处理程序,javascript将把异常当成程序错误来处理,并报告给用户。

发布了79 篇原创文章 · 获赞 304 · 访问量 51万+

猜你喜欢

转载自blog.csdn.net/liuyifeng0000/article/details/104907926