错误处理
一、What? 什么是错误处理
错误是指导致系统不能按照用户意图工作的一切原因、事件。在程序设计过程中,由于某些错误的存在,致使程序无法正常运行,处理这些错误以使程序正确运行就称为错误处理。错误处理功能是衡量编译器性能的重要方面,它在帮助程序员尽快修改程序方面起到了非常重要的作用
二、Why? 为什么需要错误处理
程序出现错误,将停止运行,这对于一个后台服务来说是致命的(在服务器上的服务是需要24小时一直运行的)。为了保证程序的健壮性与容错性,即在遇到错误时程序不会崩溃,我们需要对异常进行处理
三、How? 怎么做
(1) try、catch、finally语句
ECMA-262(JS的核心语法标准)的第三版,引入该语法:
try {
//可能导致错误的代码
}catch(e) { //捕获的错误对象
//捕获错误之后的处理代码
}finally {
//执行完所有代码必须做的一些事情,拦截try、catch的结束
}
(2) 语法
1. try不能单独并且要最先出现,其后必须紧跟catch或者finally从句不然语法错误,并且JS不支持多catch从句
你可以这么写:
try {
}finally(e) {
}
或者:
try {
}catch(e) {
if(e instanceof ***) {
//是这种错误的时候执行什么
}else if(e instanceof ***) {
}
}
但是你不能这么写:
try {
}
//OR
catch(e) {
}
//OR
finally{
}
或者:
//java的异常错误处理不能多层catch,只支持一层的catch
try {
}catch(e) {
}catch(e) {
}
2. catch语句的错误对象一定要声明,它属于语法的一部分,即使你不需要,不然发生语法错误
你可以这么写:
try {
}catch(errorReason) {//错误对象的参数名可以任意,但是一定要有
}
3. finally的强制打断机制,finally一旦出现,try和catch的return语句会失效
看一段代码:
function getCount(){
let count = 0
try {
count++
console.log(count, 'try 语句的count')
return count
count++
console.log(count, 'try 语句的count')
}catch(e) {
count++
return count
count++
console.log(count, 'catch 语句的count')
}finally {
console.log(count, 'finally 语句的count')
return count
}
}
const count = getCount()
分析结果 =>
- 当finally上层的代码(try和catch)按照逻辑执行,但是一旦遇到return,会忽略当前语句块剩余代码,并跳转执行finally的代码
- 执行finally代码时,如果遇到return语句,当前函数的返回值重新赋值为当前语句表达式结果;如果没有遇到,依然执行完成所以代码,但是返回值为之前的return的表达式的值
四、错误类型
内置错误类型(构造函数):
- Error 错误基类
- ReferenceError 引用错误(使用空指针)
- SyntaError 语法错误
- TypeError 类型错误 (类型和操作符不匹配,例如不是函数却调用)
- RangeError 超出范围、内存空间调用超过最大值
- URLError、EvalError 极少使用,根据名称就知道作用,我就不说明了
对于这些内置的错误类型的实例,浏览器能够对其识别并进行一些处理,当然也有记录错误位置的作用,这使得开发人员易于调试。
Error 语法
通过Error的构造器可以创建一个错误对象。当运行时错误产生时,Error的实例对象会被抛出。Error对象也可用于用户自定义的异常的基础对象
new Error([message[, fileName[,lineNumber]]])
-
message
可选。可阅读的错误描述信息。
-
fileName
可选。被创建的Error对象的fileName属性值。默认是调用Error构造器代码所在的文件 的名字。
-
lineNumber
可选。被创建的Error对象的lineNumber属性值。默认是调用Error构造器代码所在的文件的行号。
Error的prototype上有两个重要的属性:
- name 错误类型
- message 错误信息
- stack 错误栈信息,比message保存的多 => 错误类型、错误信息、错误代码位置(出错所在函数,一直向上直到全局环境)
Error是所以其它内置错误类型的基类
五、抛出错误异常
前面讲了catch(e) 捕获异常语句块,现在我们要讲一讲抛出异常
(1)了解基本
-
What? 什么是抛出异常
提示解析器该处语句存在开发者意料之内的异常,希望阻塞停止继续执行代码,以免造成难以弥补的错误。
-
Why? 为什么需要抛出
-
How? 怎么抛出
关键词:throw + 数据(错误原因)
值得注意的是,数据没有要求是什么类型,只要是一个结果即右值即可。这
意味着:
throw ''
throw 1
throw true
throw undefined
throw null
throw [1, 2]
throw function() {console.log('抛出的错误函数')}
throw {}
这些都是可以的,
而错误原因一般大家都叫error,因此在try-catch语句一般这么写:
try {
let error //error可以是任何的数据类型
throw error
}catch(error) { //这个error就是上面代码抛出的error的值
console.log(error)
}
catch捕获的错误error就是上面代码抛出的error的值(不考虑代码错误的情况下)
let error = {a: '1'}
try {
throw error
}catch(e) {
console.log(e, e === error)//{a: '1'} truw
}
-
assert,ECMA没有规定,但是可以自己简单实现
语法:
function assert (condition, message) {
if(!condition) {
throw new Error(message)
}
}
六、实战应用
(1) 函数封装 和 错误检测
检测参数类型,前面说过直接面对API的开发人员无法知晓内部实现,一旦发生使用错误应该抛出异常,例如
function concat(...strArr) {
let result = ''
for(let str of strArr) {
if(str) {
result += str
}
}
return result
}
上面函数存在问题就是 if 判断是,发生的类型转换,如果传入的是 0、undefined、null 他们转换为字符串不为空值,这将导致结果与预期不一致。可以做出如下修改:
function concat(...strArr) {
let result = ''
for(let [str, index] of strArr.entries()) {
if(typeof str === 'string') {
result += str
}else {
throw new Error('第' + index + '传入的参数不是string类型')
}
}
return result
}
确保连接的只是字符串
(2) 通信异常捕获和传递 <=> ajax + promise => axios
try {
$.ajax({...config})
}catch(error) {
console.log(error) // 网络资源请求错误
}
七、总结
在实际的开发中最好定义自己的错误类型,主要代码更加健壮,其次便于调试