写这篇文章的起因在于修改了一个线上问题,用户反馈点击了页面上一个功能按钮,并没有得到想要的结果。最后检查发现是由于代码执行过程中发生了错误终止了程序执行,错误没有被捕获也没有给任何反馈信息。
一、前言
对于开发人员来说,在开发功能过程中,我们会遇到各种浏览器控制台报错信息,但是通常情况下可能就是把遇到的报错解决,提交测试,部署上线等。但是产品上线给用户使用,会存在各种复杂应用和操作场景。假如我们的代码编写没有进行系统的错误捕获和异常处理,那么线上用户使用过程中肯定会遇到一些问题。这篇文章,我们来详细的认识javascript中的Error。
二、Error对象
在javascript中,错误(Error)是一个对象。由于javascript是解释执行,不存在本地编译阶段,所以在程序执行过程中,一旦发生错误,便会抛出该错误对象并停止程序执行。
const err = new Error('发生了错误')
console.log(err.message)
console.log(err.name)
console.log(err.stack)
throw err
浏览器中得到如下结果:
Error对象包含3个属性:
- message:错误消息的字符串
- name:错误的类型
- stack:错误发生的执行堆栈信息
三、错误的类型
3.1 SyntaxError 语法错误
语法错误主要指我们的开发过程中没有准守javascript的语法从而导致的错误,也是最容易修复的错误,在开发IDE中我们可以启用一些语法检查从而避免导致该错误的发生。
const name age = 18 // 变量声明语法错误
const arr = [1,2,3 // 缺少右方括号
3.2 ReferenceError 引用错误
当代码中的变量引用不正确时候会发生该错误。如先访问变量后声明、试图访问一个不可访问的变量。
console.log(name) // caught ReferenceError: Cannot access 'name' before initialization at a.html:10:17
const name = 'hello'
3.3 TypeError 类型使用错误
TypeError也是javascript程序中最常见的错误之一。当程序中的某些值得不到正确的预期时候会出现该错误。
function add(arr){
return arr.map((item) => item > 2) // TypeError: a.map is not a function
}
const result = add(1)
3.4 RangeError 范围错误
当变量设置的值超出其合法范围时候,将出现此错误。
const arr = new Array(999999999999)
console.log(arr)
3.5 URIError 编码解码错误
当我们在javascript中使用URI函数中发生错误会出现此错误,URI错误包括:encodeURI()、encodeURI()、encodeURIComponent()、encodeURIComponent()
3.6 EvalError Eval函数内部错误
在javascript中使用eval()函数发生错误时,会出现该错误。
eval('console.log(arr)')
主意,在当前的javascript引擎或ECMAScript规范中不再抛出该错误类型,但是为来向后兼容,该错误类型依然存在。
3.7 InternalError 内部错误
InternalError表示内部错误,在javascript引擎运行发生异常时抛出。如递归过深的情况下。
function loop(x) {
if (x >= 1000000000000) return;
// 做一些事情
loop(x + 1);
}
loop(0);
3.8 自定义错误类型
class CustomError extends Error {
constructor(message){
super(message)
this.name = 'CustomError'
}
}
throw new CustomError('自定义错误类型')
四、抛出错误
错误发生并被抛出才能成为异常,终止程序正常执行。抛出错误使用 throw
throw new TypeError('类型错误')
throw new ReferenceError('引用错误')
4.1 抛出错误会发生什么?
错误一旦被抛出,就会在程序调用堆栈中冒泡,直到被扑获。否则将终止程序继续执行。这里需要特别注意的是,在Promise中发生的错误如果没有被扑获,浏览器会打印出错误信息提示,但是不会退出进程终止脚本执行。即Promise内部的错误不会影响到外部代码,被称为“Promise会吃掉错误”
。
五、异常发生时的扑获处理
保证线上产品的正常运行,在开发过程中,合理正确的设计代码程序可能会发生的错误并捕获是非常重要的环节。
5.1 同步错误处理
同步代码会按照代码编写的顺序执行。可以使用try/catch/finally处理。
try {
const tom = tomName
} catch(err){
console.log(err.message)
console.log(err.name)
console.log(err.stack)
}
5.2 异步错误处理
浏览器中的异步包括:定时器、事件、Ajax、Promise等。异步中的错误不能通过try/catch捕获处理。
Promise
在promise中,catch是用来处理错误的。
function getImage(url){
return new Promise ((resolve, reject) => {
const img = new Image();
img.src = url;
img.onerror = function (){
reject(new Error('get image error'))
}
img.onload = function(){
resolve(img)
}
})
}
getImage('').then((img) => {
console.log(img)
}).catch((err) => {
console.log(err)
})
- Promise对象中的错误同样具有
冒泡
性质,会一直向后传递,至到被catch捕获。
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
-
跟传统的
try/catch
代码块不同的是,如果没有使用catch()
方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。Promise会吃掉错误
。不会终止脚本继续执行。 -
unhandledrejection事件
当 Promise
被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection
事件;
const p = new Promise((res, rej) => {
setTimeout(() => {
rej('error')
}, 3000)
})
window.addEventListener('unhandledrejection', function(err){
console.log(err)
}, false)
事件
这里事件指注册绑定的事件处理程序,如onClick,addEventListener等事件处理函数,因为他们也是异步,所以不能通过try/catch捕获。
这里有两种方法:
-
全局window.onerror(message, source, lineno, colno, error)
- message:错误信息
- source:发生错误的脚本
- lineno:发生错误的行数
- colno:发生错误的列号
- error:Error对象
window.onerror = function (msg, url, line, col, error){
console.log(msg)
console.log(url)
console.log(line)
console.log(col)
console.log(error)
}
const button = document.getElementsByTagName("button")[0]
button.addEventListener('click', function(){
const arr = new Array(999999999999)
console.log(122222)
}, false)
注意
:由于错误被window捕获,所以控制台同样会打印出错误信息。
- 虽然事件处理程序是异步执行,但其内部同样是同步执行,所以可在内部使用try/catch
const button = document.getElementsByTagName("button")[0]
button.addEventListener('click', function(){
try {
const arr = new Array(999999999999)
console.log(122222)
}catch(err){
console.log(err)
}
}, false)
定时器错误捕获
由于定时器不是同步执行,所以不能通过try/catch捕获,我们可以通过使用promise来包装定时器。
function customTimeOut(fn, ms){
return new Promise((res, rej) => {
try {
setTimeout(fn(), ms)
} catch(err){
rej(err)
}
})
}
function testError(){
throw new TypeError('type error')
}
customTimeOut(testError,1000).catch(err=>{
console.log(err)
})
六、错误捕获的价值
- 可以在出现错误时做出相应的处理,如、提示、错误上报。
- 可以防止错误未被捕获从而导致终止程序的继续执行。
- 供开发人员快速定位问题。
七、结尾
这篇文章主要介绍了javascript在浏览器中的错误类型以及如何捕获处理程序中存在的异常。希望帮助到前端的开发朋友们。时间仓促,没有来得及仔细梳理,如有错误之处,欢迎评论交流学习。
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!