关于 JavaScript 错误处理的最完整指南(上半部)

Uncaught TypeError: button is null

除了这些内置错误外,在浏览器中还有:

  • DOMException

  • DOMError,现在已经废弃,不再使用了。

DOMException是与 Web API 相关的一系列错误。 当我们在浏览器中执行愚蠢的操作时,它们会被抛出,例如:

document.body.appendChild(document.cloneNode(true));

结果:

Uncaught DOMException: Node.appendChild: May not add a Document as a child

什么是异常?


大多数开发人员认为错误和异常是一回事。实际上,错误对象只有在抛出时才会变成异常。

要在JavaScript中引发异常,我们使用throw 关键字把错误抛出去:

const wrongType = TypeError(“霍霍,好像哪里出问题了!”)

throw wrongType;

简写形式:

throw TypeError(“霍霍,好像哪里出问题了!”)

或者

throw new TypeError(“霍霍,好像哪里出问题了!”)

在函数体或者条件之外抛出异步的可能性不大,考虑下面的例子:

function toUppercase(string) {

if (typeof string !== “string”) {

throw TypeError(“霍霍,好像哪里出问题了!”);

}

return string.toUpperCase();

}

这里我们检查函数参数是否为字符串。如果不是,我们抛出一个异常。从技术上讲,JavaScript中可以抛出任何东西,而不仅仅是错误对象

throw Symbol();

throw 33;

throw “Error!”;

throw null;

但是,最好避免这些事情:始终抛出正确的错误对象,而不是一些基本类型。

这样有助于在代码中,错误处理的一致性。 其他成员可以期望在错误对象上访问error.messageerror.stack 来知道错误的源头。

当我们抛出异常时会发生什么?


异常就像一个上升的电梯:一旦你抛出一个,它就会在程序堆栈中冒泡,除非它在某个地方被捕获。

考虑以下代码:

function toUppercase(string) {

if (typeof string !== “string”) {

throw TypeError(“参数类型需要是 string 的”);

}

return string.toUpperCase();

}

toUppercase(4);

运行代码会在控制台看到:

Uncaught TypeError: Wrong type given, expected a string

toUppercase http://localhost:5000/index.js:3

http://localhost:5000/index.js:9

可以看到发生错误的确切行。

这个报告是一个堆栈跟踪,它有助于跟踪代码中的问题。堆栈跟踪从下至上:

toUppercase http://localhost:5000/index.js:3

http://localhost:5000/index.js:9

除了在浏览器的控制台中看到此堆栈跟踪外,还可以通过错误对象的stack属性进行查看。

如果异常未被捕获,也就是说,程序员不采取任何措施来捕获它,程序将崩溃。

何时何地捕获代码中的异常取决于特定的用例。

例如,我们可能想在堆栈中传递一个异常,以使程序完全崩溃。这种情况发生在, 让错误停止程序比处理无效数据来得更安全。

接下来,我们来看看 JavaScript 同步和异步中的错误和异常处理。

同步中的错误处理


同步代码在大多数情况下都很简单,因此它的错误处理也很简单。

常规函数的错误处理

同步代码的执行顺序与写入顺序相同。我们再看一下前面的例子:

function toUppercase(string) {

if (typeof string !== “string”) {

throw TypeError(“参数类型需要是 string 的”);

}

return string.toUpperCase();

}

toUppercase(4);

在这里,引擎调用并执行toUppercase。 所有这些都是同步发生的。 要捕获同步函数引发的异常,我们可以使用try/catch/finally

try {

toUppercase(4);

} catch (error) {

console.error(error.message);

} finally {

}

try/catch/finally是一个同步结构,但它也可以捕获异步出现的异常。

使用 generator 函数来处理错误


JavaScript中的生成器函数是一种特殊的函数。除了在其内部作用域和使用者之间提供双向通信通道之外,还可以随意暂停恢复

要创建一个生成器函数,我们在function关键字后面放一个*:

function* generate() {

//

}

在函数内可以使用yield返回值:

function* generate() {

yield 33;

yield 99;

}

生成器函数的返回值是一个迭代器对象(iterator object)。要从生成器中提取值,我们可以使用两种方法:

  • 使用 next() 方法

  • 通过 for...of 遍历

如下所示,要想在生成器中获取值,我们可以这样做:

function* generate() {

yield 33;

yield 99;

}

const go = generate();

const firstStep = go.next().value; // 33

const secondStep = go.next().value; // 99

成器也可以采用其他方法工作:它们可以接收调用者返回的值和异常。

除了next()之外,从生成器返回的迭代器对象还具有throw()方法。使用这种方法,我们可以通过向生成器中注入一个异常来停止程序

function* generate() {

yield 33;

yield 99;

}

const go = generate();

const firstStep = go.next().value; // 33

go.throw(Error(“我要结束你!”));

const secondStep = go.next().value; // 这里会抛出异常

要获取此错误,可以在生成器函数中使用 try/catch/finally:

function* generate() {

try {

yield 33;

yield 99;

} catch (error) {

console.error(error.message);

}

}

下面这个事例是使用 for...of 来获取 生成器函数中的值:

function* generate() {

yield 33;

yield 99;

throw Error(“我要结束你!”)

}

try {

for (const value of generate()) {

console.log(value)

}

} catch (error) {

console.log(error.message)

}

/* 输出:

33

99

我要结束你!

*/

异步中的错误处理


JavaScript本质上是同步的,是一种单线程语言。

诸如浏览器引擎之类的宿主环境使用许多Web API, 增强了 JS 以与外部系统进行交互并处理与 I/O 绑定的操作。

浏览器中异步操作有:定时器相关的函数、事件和 Promise。

异步中的错误处理不同于同步的错误处理。我们来看一些例子。

定时器的错误处理

考虑下面的代码片段:

function failAfterOneSecond() {

setTimeout(() => {

throw Error(“Something went wrong!”);

}, 1000);

}

这个函数大约在1秒后抛出异常,处理这个异常的正确方法是什么?

下面的方法不起作用:

function failAfterOneSecond() {

setTimeout(() => {

throw Error(“Something went wrong!”);

}, 1000);

}

try {

failAfterOneSecond();

} catch (error) {

console.error(error.message);

}

我们知道 try/catch 是同步,而 setTimeout 是异步的。当执行到 setTimeout回调时,try/catch 早已跑完了,所以异常就无法捕获到。

它们在两务不同的轨道上:

Track A: --> try/catch

Track B: --> setTimeout --> callback --> throw

如果能让程序跑下去,把 try/catch 移动到 setTimeout 里面。但这种做法意义不大,后面我们会使用 Promise 来解决这类的问题。

事件中错误处理

DOM 的事件操作(监听和触发),都定义在EventTarget接口。Element节点、document节点和window对象,都部署了这个接口。此外,XMLHttpRequest、AudioNodeAudioContext等浏览器内置对象,也部署了这个接口。该接口就是三个方法,addEventListenerremoveEventListener用于绑定和移除监听函数,dispatchEvent用于触发事件。

DOM 事件的错误处理机制遵循任何异步Web API的相同方案。

考虑下面示例:

const button = document.querySelector(“button”);

button.addEventListener(“click”, function() {

throw Error(“Can’t touch this button!”);

});

在这里,单击按钮后立即引发异常。 我们如何抓住它? 下面这种方式没啥作用,也不会阻止程序崩溃:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
;

在这里,单击按钮后立即引发异常。 我们如何抓住它? 下面这种方式没啥作用,也不会阻止程序崩溃:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-wJamnlHC-1715830901688)]

[外链图片转存中…(img-cFOb3c7o-1715830901689)]

[外链图片转存中…(img-0cFllzzr-1715830901689)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值