资讯专栏INFORMATION COLUMN

前端开发中的Error以及异常捕获

Mr_houzi / 3431人阅读

摘要:前端开发中的中的中,是一个构造函数,通过它创建一个错误对象。是核心对象,表示调用一个时发生的异常。将回调函数包裹一层接下来可以将统一进行处理。中的错误捕获在以前,可以使用来处理捕获的错误。研究结果在这里中的错误捕获的源码中,在关

本文首发于公众号:符合预期的CoyPan
写在前面

在前端项目中,由于JavaScript本身是一个弱类型语言,加上浏览器环境的复杂性,网络问题等等,很容易发生错误。做好网页错误监控,不断优化代码,提高代码健壮性是一项很重要的工作。本文将从Error开始,讲到如何捕获页面中的异常。文章较长,细节较多,请耐心观看。

前端开发中的Error JavaScript中的Error

JavaScript中,Error是一个构造函数,通过它创建一个错误对象。当运行时错误产生时,Error的实例对象会被抛出。构造一个Error的语法如下:

// message: 错误描述
// fileName: 可选。被创建的Error对象的fileName属性值。默认是调用Error构造器代码所在的文件的名字。
// lineNumber: 可选。被创建的Error对象的lineNumber属性值。默认是调用Error构造器代码所在的文件的行号。

new Error([message[, fileName[, lineNumber]]])
ECMAScript标准:

Error有两个标准属性:

Error.prototype.name :错误的名字

Error.prototype.message:错误的描述

例如,在chrome控制台中输入以下代码:

var a = new Error("错误测试");
console.log(a); // Error: 错误测试
                // at :1:9
console.log(a.name); // Error
console.log(a.message); // 错误测试

Error只有一个标准方法:

Error.prototype.toString:返回表示一个表示错误的字符串。

接上面的代码:

a.toString();  // "Error: 错误测试"
非标准的属性

各个浏览器厂商对于Error都有自己的实现。比如下面这些属性:

Error.prototype.fileName:产生错误的文件名。

Error.prototype.lineNumber:产生错误的行号。

Error.prototype.columnNumber:产生错误的列号。

Error.prototype.stack:堆栈信息。这个比较常用。

这些属性均不是标准属性,在生产环境中谨慎使用。不过现代浏览器差不多都支持了。

Error的种类

除了通用的Error构造函数外,JavaScript还有7个其他类型的错误构造函数。

InternalError: 创建一个代表Javascript引擎内部错误的异常抛出的实例。 如: "递归太多"。非ECMAScript标准。

RangeError: 数值变量或参数超出其有效范围。例子:var a = new Array(-1);

EvalError: 与eval()相关的错误。eval()本身没有正确执行。

ReferenceError: 引用错误。 例子:console.log(b);

SyntaxError: 语法错误。例子:var a = ;

TypeError: 变量或参数不属于有效范围。例子:[1,2].split(".")

URIError: 给 encodeURI或 decodeURl()传递的参数无效。例子:decodeURI("%2")

当JavaScript运行过程中出错时,会抛出上8种(上述7种加上通用错误类型)错误中的其中一种错误。错误类型可以通过error.name拿到。

你也可以基于Error构造自己的错误类型,这里就不展开了。

其他错误

上面介绍的都是JavaScript本身运行时会发生的错误。页面中还会有其他的异常,比如错误地操作了DOM。

DOMException

DOMException是W3C DOM核心对象,表示调用一个Web Api时发生的异常。什么是Web Api呢?最常见的就是DOM元素的一系列方法,其他还有XMLHttpRequest、Fetch等等等等,这里就不一一说明了。直接看下面一个操作DOM的例子:

var node = document.querySelector("#app");
var refnode = node.nextSibling;
var newnode = document.createElement("div");
node.insertBefore(newnode, refnode);

// 报错:Uncaught DOMException: Failed to execute "insertBefore" on "Node": The node before which the new node is to be inserted is not a child of this node.

单从JS代码逻辑层面来看,没有问题。但是代码的操作不符合DOM的规则。

DOMException构造函数的语法如下:

// message: 可选,错误描述。
// name: 可选,错误名称。常量,具体值可以在这里找到:https://developer.mozilla.org/zh-CN/docs/Web/API/DOMException

new DOMException([message[, name]]);

DOMException有以下三个属性:

DOMException.code:错误编号。

DOMException.message:错误描述。

DOMException.name:错误名称。

以上面那段错误代码为例,其抛出的DOMException各属性的值为:

code: 8
message: "Failed to execute "insertBefore" on "Node": The node before which the new node is to be inserted is not a child of this node."
name: "NotFoundError"
Promise产生的异常

Promise中,如果Promisereject了,就会抛出异常:PromiseRejectionEvent。注意,下面两种情况都会导致Promisereject

业务代码本身调用了Promise.reject

Promise中的代码出错。

PromiseRejectionEvent的构造函数目前在浏览器中大多都不兼容,这里就不说了。

PromiseRejectionEvent的属性有两个:

PromiseRejectionEvent.promise:被rejectPromise

PromiseRejectionEvent.reasonPromisereject的原因。会传递给rejectPromsiecatch中的参数。

加载资源出错

由于网络,安全等原因,网页加载资源失败,请求接口出错等,也是一种常见的错误。

关于错误的小结

一个网页在运行过程中,可能发生四种错误:

JavaScript在运行过程,语言自身抛出的异常。

JavaScript在运行过程中,调用Web Api时发生异常。

Promise中的拒绝。

网页加载资源,调用接口时发生异常。

我认为,对于前两种错误,我们在平时的开发过程中,不用特别去区分,可以统一成:【代码出错】。

捕获错误

网页发生错误,开发者如何捕获这些错误呢 ? 常见的有以下方法。

try...catch...

try...catch…大家都不陌生了。一般用来在具体的代码逻辑中捕获错误。

try {
  throw new Error("oops");
}
catch (ex) {
  console.log("error", ex.message); // error oops
}

try-block中的代码发生异常时,可以在catck-block中将异常接住,浏览器便不会抛出错误。但是,这种方式并不能捕获异步代码中的错误,如:

try {
    setTimeout(function(){
        throw new Error("lala");
    },0);
} catch(e) {
    console.log("error", e.message);
}

这个时候,浏览器依然会抛出错误:Uncaught Error: lala

试想以下,如果我们将所有的代码合理的划分,然后都用try catch包起来,是不是就可以捕获到所有的错误了呢?可以通过编译工具来实现这个功能。不过,try catch是比较耗费性能的。

window.onerror
window.onerror = function(message, source, lineno, colno, error) { ... }

函数参数:

message:错误信息(字符串)

source:发生错误的脚本URL(字符串)

lineno:发生错误的行号(数字)

colno:发生错误的列号(数字)

error:Error对象(对象)

注意,如果这个函数返回true,那么将会阻止执行浏览器默认的错误处理函数。

window.addEventListener("error")
window.addEventListener("error", function(event) { ... })

我们调用Object.prototype.toString.call(event),返回的是[object ErrorEvent]。可以看到eventErrorEvent对象的实例。ErrorEvent是事件对象在脚本发生错误时产生,从Event继承而来。由于是事件,自然可以拿到target属性。ErrorEvent还包括了错误发生时的信息。

ErrorEvent.prototype.message: 字符串,包含了所发生错误的描述信息。

ErrorEvent.prototype.filename: 字符串,包含了发生错误的脚本文件的文件名。

ErrorEvent.prototype.lineno: 数字,包含了错误发生时所在的行号。

ErrorEvent.prototype.colno: 数字,包含了错误发生时所在的列号。

ErrorEvent.prototype.error: 发生错误时所抛出的 Error 对象。

注意,这里的ErrorEvent.prototype.error对应的Error对象,就是上文提到的Error, InternalErrorRangeErrorEvalErrorReferenceErrorSyntaxErrorTypeErrorURIErrorDOMException中的一种。

window.addEventListener("unhandledrejection")
window.addEventListener("unhandledrejection", function (event) { ... });

在使用Promise的时候,如果没有声明catch代码块,Promise的异常会被抛出。只能通过这个方法或者window.onunhandledrejection才能捕获到该异常。

event就是上文提到的PromiseRejectionEvent。我们只需要关注其reason就行。

window.onerror 和 window.addEventListener("error")的区别

首先是事件监听器事件处理器的区别。监听器只能声明一次,后续的声明会覆盖之前的声明。而事件处理器则可以绑定多个回调函数。

资源(

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100603.html

相关文章

  • 前端异常监控-看这篇就够了

    摘要:前端异常监控如果是移除的流程,那么编程就一定是将放进去的流程。过滤掉运行时错误上报加载错误事件捕获异常最新的规范中定义了事件用于全局捕获对象没有处理器时异常情况。 前端异常监控 如果debug是移除bug的流程,那么编程就一定是将bug放进去的流程。如果没有用户反馈问题,那就代表我们的产品棒棒哒,对不对? 主要内容 Web规范中相关前端异常 异常按照捕获方式分类 异常的捕获方式 日志...

    Aklman 评论0 收藏0
  • 详解JS错误处理:前端JS/Vue/React/Iframe/跨域/Node

    摘要:错误上报机制发送数据因为请求本身也有可能会发生异常,而且有可能会引发跨域问题,一般情况下更推荐使用动态创建标签的形式进行上报。 js错误捕获 js错误的实质,也是发出一个事件,处理他 error实例对象 对象属性 message:错误提示信息 name:错误名称(非标准属性)宿主环境赋予 stack:错误的堆栈(非标准属性)宿主环境赋予 对象类型(7种) Synt...

    张利勇 评论0 收藏0
  • 前端魔法堂——异常不仅仅是try/catch

    摘要:我打算分成前端魔法堂异常不仅仅是和前端魔法堂调用栈,异常实例中的宝藏两篇分别叙述内置自定义异常类,捕获运行时异常语法异常网络请求异常事件,什么是调用栈和如何获取调用栈的相关信息。 前言  编程时我们往往拿到的是业务流程正确的业务说明文档或规范,但实际开发中却布满荆棘和例外情况,而这些例外中包含业务用例的例外,也包含技术上的例外。对于业务用例的例外我们别无它法,必须要求实施人员与用户共同...

    bladefury 评论0 收藏0
  • 前端性能与异常上报

    摘要:回过头来发现,我们的项目,虽然在服务端层面做好了日志和性能统计,但在前端对异常的监控和性能的统计。对于前端的性能与异常上报的可行性探索是有必要的。这是我们页面加载性能优化需求中主要上报的相关信息。 概述 对于后台开发来说,记录日志是一种非常常见的开发习惯,通常我们会使用try...catch代码块来主动捕获错误、对于每次接口调用,也会记录下每次接口调用的时间消耗,以便我们监控服务器接口...

    gggggggbong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<