The content of this article is about the analysis of errors and exceptions in JavaScript (with examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you. help.
My advice is don't hide errors, be brave enough to throw them out. No one should be ashamed if a bug in the code causes the program to crash. We can interrupt the program and let the user start over. Mistakes are inevitable, how you deal with them is what matters.
JavaScript provides a set of error handling mechanisms. Errors are abnormal accidents that interfere with the normal flow of the program. And no one can keep the program bug-free. So if you encounter a special bug after going online, how can you locate the problem faster? This is what our topic needs to discuss.
The following will describe the basic knowledge of JavaScript Error, how to intercept and capture exceptions, and how to conveniently report errors online. I also made some summary and analysis based on the knowledge points on the Internet (I am just a porter of the Internet) (worker, not creator)), if there are any errors or omissions, please criticize me severely on the issue.
This topic is currently aimed at browsers, and node.js has not been taken into account. However, they are all JavaScript Es6 syntax, which is similar.
When does JavaScript throw an error?
Generally divided into two situations:
JavaScript comes with errors
Errors actively thrown by developers
JavaScript engine Automatically thrown errors
The errors we encounter in most scenarios are this type of errors. If a Javscript syntax error, code reference error, type error, etc. occurs, the JavaScript engine will automatically trigger such errors. Some scenarios are as follows:
Scenario 1
console.log(a.notExited) // 浏览器会抛出 Uncaught ReferenceError: a is not defined
Scenario 2
const a; // 浏览器抛出 Uncaught SyntaxError: Missing initializer in const declaration
Syntax error, the browser usually throws the error as soon as possible and will not wait until execution Report an error.
Scenario 3
let data; data.forEach(v=>{}) // Uncaught TypeError: Cannot read property 'forEach' of undefined
Manually thrown errors
are generally custom error exceptions developed by class libraries (such as illegal error exceptions such as parameters are thrown). Or re-modify the error message and report it to facilitate understanding.
Scene One
function sum(a,b){ if(typeof a !== 'number') { throw TypeError('Expected a to be a number.'); } if(typeof b !== 'number') { throw TypeError('Expected b to be a number.'); } return a + b; } sum(3,'d'); // 浏览器抛出 uncaught TypeError: Expected b to be a number.
Scenario Two
Of course we don’t necessarily need to do this.
let data; try { data.forEach(v => {}); } catch (error) { error.message = 'data 没有定义,data 必须是数组。'; error.name = 'DataTypeError'; throw error; }
How to create an Error object?
The creation syntax is as follows:
new Error([message[,fileName,lineNumber]])
The same is true for omitting the new syntax.
fileName and lineNumber are not compatible with all browsers, and Google does not support them, so they can be ignored.
The Error constructor is a general error type. In addition to the Error type, there are also TypeError, RangeError and other types.
Error Example
Listed here are the prototype chain properties and methods of the Error layer. The inheritance properties and convenience of the deeper prototype chain are not explained. Some compatible and uncommon properties and methods are not explained.
console.log(Error.prototype) // 浏览器输出 {constructor: ƒ, name: "Error", message: "", toString: ƒ}
Other error type constructors inherit Error, and the instances are consistent.
Properties
Error.prototype.message
Error message, Error("msg").message === "msg".
Error.prototype.name
Error type (name), Error("msg").name === "Error". If TypeError, then name is TypeError.
Error.prototype.stack
The Error object provides a function tracing method as a non-standard stack attribute. No matter what mode the function is called from, from which line or file, and with what parameters. This stack is generated from the earliest recent call, returning the original global scope call.
This is not a specification, there is compatibility. After testing, Google, Firefox, Edge, and Safari all support this feature (all tested under the latest version 2019-04-02), but IE does not support it.
Method
Error.prototype.constructor
Error.prototype.toString
The return value format is ${name}: ${message}.
Commonly used Error types
In addition to the general Error constructor, JavaScript also has 5 common other types of error constructors.
TypeError
Create an Error instance to indicate the cause of the error: the variable or parameter does not belong to a valid type.
throw TypeError("类型错误"); // Uncaught TypeError: 类型错误
RangeError
Create an Error instance to indicate the cause of the error: the numerical variable or parameter exceeds its valid range.
throw RangeError("数值超出有效范围"); // Uncaught RangeError: 数值超出有效范围
ReferenceError
Create an Error instance to indicate the cause of the error: invalid reference.
throw ReferenceError("无效引用"); // Uncaught ReferenceError: 无效引用
SyntaxError
Create an Error instance to indicate the cause of the error: syntax error. This scenario is rarely used unless the class library defines a new syntax (such as template syntax).
throw SyntaxError("语法错误"); // Uncaught SyntaxError: 语法错误
URIError
Create an Error instance to indicate the cause of the error: uri-related errors.
throw URIError("url 不合法"); // Uncaught RangeError: url 不合法
自定义 Error 类型
自定义新的 Error 类型需要继承 Error ,如下自定义 CustomError:
function CustomError(...args){ class InnerCustomError extends Error { name = "CustomError"; } return new InnerCustomError(...args); }
继承 Error 后,我们只需要对 name 做重写,然后封装成可直接调用的函数即可。
如何拦截 JavaScript 错误?
既然没人能保证 web 应用不会出现 bug,那么出现异常报错时,如何拦截并进行一些操作呢?
try…catch… 拦截
这是拦截 JavaScript 错误,拦截后,如果不手动抛出错误,这个错误将静默处理。平常写代码如果我们知道某段代码可能会出现报错问题,就可以使用这种方式。如下:
const { data } = this.props; try { data.forEach(d=>{}); // 如果 data 不是数组就会报错 } catch(err){ console.error(err); // 这里可以做上报处理等操作 }
一些使用方式
十分不友好的处理方式
try...catch... 使用需要注意,try…catch… 后,错误会被拦截,如果不主动抛出错误,那么无法知道报错位置。如下面的处理方式就是不好的。
function badHandler(fn) { try { return fn(); } catch (err) { /**noop,不做任何处理**/ } return null; } badHandler();
这样 fn 回调发送错误后,我们无法知道错误是在哪里发生的,因为已经被 try…catch 了,那么如何解决这个问题呢?
function CustomError(...args){ class InnerCustomError extends Error { name = "CustomError"; } return new InnerCustomError(...args); } function uglyHandlerImproved(fn) { try { return fn(); } catch (err) { throw new CustomError(err.message); } return null; } badHandler();
现在,这个自定义的错误对象包含了原本错误的信息,因此变得更加有用。但是因为再度抛出来,依然是未处理的错误。
try…catch… 可以拦截异步错误吗?
这个也要分场景,也看个人的理解方向,首先理解下面这句话:
try…catch 只会拦截当前执行环境的错误,try 块中的异步已经脱离了当前的执行环境,所以 try…catch… 无效。
setTimeout 和 Promise 都无法通过 try…catch 捕获到错误,指的是 try 包含异步(非当前执行环境),不是异步包含 try(当前执行环境)。异步无效和有效 try…catch 如下:
setTimeout
这个无效:
try { setTimeout(() => { data.forEach(d => {}); }); } catch (err) { console.log('这里不会运行'); }
下面的 try…catch 才会有效:
setTimeout(() => { try { data.forEach(d => {}); } catch (err) { console.log('这里会运行'); } });
Promise
这个无效:
try { new Promise(resolve => { data.forEach(d => {}); resolve(); }); } catch (err) { console.log('这里不会运行'); }
下面的 try…catch 才会有效:
new Promise(resolve => { try { data.forEach(d => {}); } catch (err) { console.log('这里会运行'); } });
小结
不是所有场景都需要 try…catch… 的,如果所有需要的地方都 try…catch,那么代码将变得臃肿,可读性变差,开发效率变低。那么我需要统一获取错误信息呢?有没有更好的处理方式?当然有,后续会提到。
Promise 错误拦截
Promise.prototype.catch 可以达到 try…catch 一样的效果,只要是在 Promise 相关的处理中报错,都会被 catch 到。当然如果你在相关回调函数中 try…catch,然后做了静默提示,那么也是 catch 不到的。
如下会被 catch 到:
new Promise(resolve => { data.forEach(v => {}); }).catch(err=>{/*这里会运行*/})
下面的不会被 catch 到:
new Promise(resolve => { try { data.forEach(v => {}); }catch(err){} }).catch(err=>{/*这里不会运行*/})
Promise 错误拦截,这里就不详细说了,如果你看懂了 try…catch,这个也很好理解。
setTimeout 等其他异步错误拦截呢?
目前没有相关的方式直接拦截 setTimeout 等其他异步操作。
如果要拦截 setTimeout 等异步错误,我们需要在异步回调代码中处理,如:
setTimeout(() => { try { data.forEach(d => {}); } catch (err) { console.log('这里会运行'); } });
这样可以拦截到 setTimeout 回调发生的错误,但是如果是下面这样 try…catch 是无效的:
try { setTimeout(() => { data.forEach(d => {}); }); } catch (err) { console.log('这里不会运行'); }
如何获取 JavaScript 错误信息?
你可以使用上面拦截错误信息的方式获取到错误信息。但是呢,你要每个场景都要去拦截一遍吗?首先我们不确定什么地方会发生错误,然后我们也不可能每个地方都去拦截错误。
不用担心,JavaScript 也考虑到了这一点,提供了一些便捷的获取方式(不是拦截,错误还是会终止程序的运行,除非主动拦截了)。
window.onerror 事件获取错误信息
onerror 事件无论是异步还是非异步错误(除了 Promise 错误),onerror 都能捕获到运行时错误。
需要注意一下几点:
window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx。如果使用 addEventListener,event.preventDefault() 可以达到同样的效果。
window.onerror 是无法捕获到网络异常的错误、或