错误处理与调试
浏览器错误报告
所有主流桌面浏览器都提供了向用户报告错误的机制;默认情况下,所有浏览器都会隐藏错误信息;因为除了开发者外这些信息对其他人没有什么用,还有就是网页在正常操作中报错的固有特性
桌面控制台
所有的现代桌面浏览器都会通过控制台暴露错误,不同浏览器进入控制台方式不一样,详情看红宝书 p675
移动控制台
浏览器不会直接在移动设备上提供控制台界面
错误处理
有良好的错误处理策略可以让用户知道发生了什么;为此必须理解各种捕获和处理 JavaScript 错误方式
try/catch 语句
ECMA-262 新增了 try/catch 语句,作为在 JavaScript 中处理异常的一种方式
如果 try 块中有代码发生错误,代码会立即退出执行,并跳到 catch 中;catch 会接收到一个对象,该对象包含发生的错误相关信息;与其他语言不同,即使在 catch 块中不使用错误对象,也必须为它定义名称;错误对象中暴露的实际信息因浏览器而异,但是少包含错误消息的 message 属性;ECMA-262 也指定了错误类型的 name 属性
1、finally 子句
finally 中的代码始终运行,try 或 catch 中的代码无法阻止,包括 return 语句
2、错误类型
Error:基类型,其他的错误都继承该类型;浏览器很少会抛出该类型错误,该类型主要用于开发者抛出自定义错误
InternalError:底层 js 引擎发生抛出异常时由浏览器抛出
EvalError:eval()的使用与定义不一致
RangeError:数值越界
ReferenceError:非法或不能识别的引用值
SyntaxError:发生语法解析错误
TypeError:操作数据类型错误
URIError:URI 处理函数使用不当(encodeURI()和 decodeURI())
3、try/catch 用法
一般来说,在运用了无法修改的可能有问题的代码时,就可以用该语句,但是当明确了某个错误很可能会发生时,建议修改代码防止错误而不是使用该语句捕获错误
抛出错误
与 try/catch 对应的机制是 throw 操作符,用于在任何时候抛出自定义错误,throw 操作符必须有一个值,但是值的类型不限
throw 123;
throw "hello";
throw true;
throw { name: "lsn" };
使用 throw 时,代码立即停止执行,除非使用 try/catch 捕获了抛出的值
可以通过内置的错误类型来模拟浏览器错误,每种错误类型的构造函数都只接收一个参数,就是错误消息
throw new Error('xxx');
throw new TypeError('xxx');
通过继承 Error 可以自定义错误类型,需要提供那么属性和 message 属性
1、何时抛出错误
在某个代码中自定义错误并给出错误位置提示可以有效地找到错误相关代码
2、抛出错误与 try/catch
捕获错误的目的是阻止浏览器以其默认方式响应;抛出错误的目的是为错误提供有关其发生的原因说明
error 事件
任何没有被 try/catch 语句处理的错误都会在 window 对象上触发 error 事件
在 onerror 事件处理程序中,会传入三个参数:错误消息、发生错误的 URL 和行号
onerror 事件处理程序需要使用 DOM Level 0 技术来指定
可以在 onerror 事件处理程序中返回 false 来阻止浏览器默认报告错误的行为
这代表浏览器错误最后一道防线,如果使用 try/catch 得当,浏览器应该不会到达这个事件
不同浏览器中对于这个事件的处理机制不一样(红宝书 p682)
图片也支持 error 事件,在图片 src 属性中的 URL 没有返回可识别的图片格式时触发
错误处理策略
作为开发者,应当知晓自己的代码在什么情况下会失败,以及失败会导致什么结果;另外还要有一个系统来跟踪这些问题
识别错误
因为 js 是松散类型的,不会验证函数参数,所以有些代码只有运行起来才能知道错误;通常也要注意 3 类错误:
类型转换错误
数据类型错误
通信错误
1、静态代码分析器
在代码构建流程中添加静态代码分析或检查器(linter),可以预先发现非常多错误
常用的工具是 JSHint、JSLint、Google Closure、TypeScript;静态代码分析器要求使用类型、函数签名以及其他指令来注解 js;分析器会通过注解和 js 代码的各个部分,对实际运行时可能出现的潜在不兼容问题给出提醒
2、类型转换错误
通过使用严格不相等可以预防这种错误;if 语句中如果使用typeof xxx === "string"
这种强制比较,也可以预防这种错误
3、数据类型错误
在使用某数据类型时,先判断其类型正确,再使用,可以防止类型错误报错
4、通信错误
对于查询字符串,应该使用 encodeURIComponent()编码
区分重大与非重大错误
具有一个或多个特性的错误属于非重大错误:
不会影响用户的主要任务
只会影响页面中某个部分
可以恢复
重复操作可能成功
本质上,不需要担心非重大错误
重大错误具备如下特性:
应用程序绝对无法继续运行
错误严重影响了用户的主要目标
会导致其他错误发生
如果发生重大错误,必须要通知用户,告知用户不能使用或者需要刷新页面等
把错误记录到服务器中
Web 应用程序开发中的一个常见的做法是建立中心化的错误日志存储和跟踪系统
function logError(sev, msg) {
let img = new Image(),
encodedSev = encodeURIComponent(sev),
encodedMsg = encodeURIComponent(msg);
img.src = "log.php?sev=${encodedSev}&msg=${encodedMsg}";
}
这个函数接收两个参数:严重程度、错误消息
在 catch 中使用 logError 函数向服务器发送错误日志;这里使用 Image 对象发送,因为其灵活性:所有浏览器都支持、不受跨域规则限制、记录错误的过程很少出错
调试技术
把消息记录到控制台
所有主流浏览器都有 js 控制台,通过 console 对象直接把 js 消息写入控制台,其包含如下方法:
error(message):在控制台中记录错误信息
info(message):在控制台中记录信息性内容
log(message):在控制台中记录常规消息
warn(message):在控制台中记录警告消息
理解控制台运行时
浏览器控制台是个读取-求值-打印-循环,与页面 js 运行时并发
在 Element 元素标签页中选中一个元素,可以在控制台使用$0 来访问
使用 JavaScript 调试器
ECMAScript 5.1 规范定义了 debugger 关键字,用于调用可能存在的调试功能;这个功能等同于断点
浏览器也支持在开发者工具的源代码页中选择希望设置断点的代码行来手动设置断点;这样子设置的断点与使用 debugger 关键字一样,只是不会在不同浏览器会话中保存
在页面中打印消息
可以把消息写到页面某个指定区域
补充控制台方法
可以自定义 console.log 方法,赋给其一个自定义函数;但是这种方法在页面刷新后会失效
抛出错误
手动检查错误并且抛出具体信息,会比浏览器默认抛出的错误要更具体
在大型应用程序中,通常使用 assert()函数抛出错误
//目标函数
function test(num1, num2) {
assert(typeof num1 == "number" && typeof num2 == "number", "test(): arguments wrong");
return num1 / num2;
}
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
旧版 IE 常见错误
IE 曾是最难调试的 JavaScript 错误的浏览器之一,该方案可以了解,因为 IE 现如今已经不受支持
无效字符
JavaScript 代码必须由特定字符构成;在检测到 JavaScript 文件中存在无效字符时,IE 会抛出“invalid character”错误
例如看起来像减号但是又不是减号的字符(Unicode 值为/u2013),FIrefox、Safari、Opera 都会报错
未找到成员
旧版 IE 中所有 DOM 对象都是用 COM 对象实现的,并非原生 JavaScript 对象;在涉及垃圾回收时,这可能会导致很多奇怪的行为
例如,event 对象,IE 的 event 是作为 window 的一个属性存在的,事件完成后就会被销毁,在之后的闭包中使用,尝试给 event 对象赋值就会发生错误
未知运行时错误
使用 innerHTML 和 outerHTML 属性以下面一种方式添加 HTML 时会发生运行时错误:将块级元素插入行内元素、在表格任何部分访问了其中一个属性
语法错误
通常 IE 报告语法错误,原因是很清楚的
但是当网页中引入了一个外部 js 文件由于某种原因返回了非 js 代码,IE 会抛出错误,报告该语法错误发生在脚本第一行的第一个字符;Opera 和 Safari 也会报告语法错误,但是会报告是引用文件不当导致的问题;Firefox 会忽略作为 js 引用非 js 文件导致的解析错误
系统找不到指定资源
还有一个可能最没用的消息:“The system cannot locate the resource specified”(系统找不到指定资源);这个错误会在 js 发送请求而 URL 长度超过了 IE 允许的最大 URL 长度(2083 个字符)时发生
IE 对 URL 路径还有 2048 个字符的限制