一、错误这个东西
-
太多的时候javascript报出的错误,都是没有上下文的.比如object expected.非常难调试.
-
此时就需要我们自定义错误处理机制,清晰明确的搞明白错误的位置.
二、错误类型
1.常见错误
- SyntaxError(语法错误)
解析代码时发生的语法错误
var 1a // Uncaught SyntaxError: Invalid or unexpected token
- Uncaught ReferenceError:引用错误
引用了不存在的变量
a() // Uncaught ReferenceError: a is not defined
console.log(b) // Uncaught ReferenceError: b is not defined
- RangeError:范围错误,当值超出有效范围时发生的错误
Number对象的方法参数超出范围
var num = new Number(12.34)
console.log(num.toFixed(-1)) // Uncaught RangeError: toFixed() digits argument must be between 0 and 20 at Number.toFixed
- TypeError 类型错误
变量或参数不是预期类型时发生的错误,或者访问不存在的方法.
let obj = {};
obj.say() // Uncaught TypeError: obj.say is not a function
let o = new 10;
- URIError,URL错误
decodeURI("%"); // Uncaught URIError: URI malformed
该错误,主要是以下几个函数涉及的 encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()
- ReferenceError 引用错误
当一个不存在的变量被引用时发生的错误
var a = 10;
console.log(a);
console.log(b); // Uncaught ReferenceError: b is not defined
- EvalError eval()函数执行错误,ES5+基本看不到的错误.
Error是基类型,其它错误都继承该类型,故所有的错误都共享相同的属性.
Error一般浏览器不会抛出,主要用于开发者抛出自定义错误.
new Error([message[,fileName[,lineNumber]]])
第一个参数表示错误提示信息,第二个是文件名,第三个是行号。
三、错误捕获
1. 错误定义和抛出
- throw 操作符用于抛出自定义错误.
- 必须有一个值,但类型不限.
- 一旦抛出错误,代码就会停止执行.
throw 12345;
throw 'Hello world';
throw new Error('some bad');
throw new SyntaxError('语法错误');
throw new TypeError('类型错误');
throw new RangeError('值超出有效范围');
throw new URIError('URI错误')
throw new ReferenceError('引用错误');
- 通过继承Error可以自定义错误类型
class customError extends Error {
constructor(message) {
super(message);
this.name = 'customError';
this.message = message;
}
}
throw new customError('自定义错误类型')
2. 代码中怎么抛出
- 如果是有几千行代码的web应用程序,想找到错误就比较难,此时可以使用自定义错误,有效的提高代码的可维护性.
- 抛出错误的目的,是为错误提供有关其发生原因的说明
function process(values) {
if (!(values instanceof Array)) {
throw new Error('process(): Argument must be an array');
}
values.sort();
}
process(123)
3. 捕获错误
- 应该在明确知道一旦错误了,后面要怎么做时,才去捕获错误.
- 捕获错误的目的在于阻止浏览器以其默认方式相应.
try{
// 可能出错的代码块
}catch(error){
// 出错的时候要做什么
}
- 调用不存在的函数
try {
fn()
} catch (error) {
console.log('An error happened', error);
}
4. error事件
- 凡是没有被try/catch语句处理的错误都会触发window.error事件.
// html代码
<p onclick="fn()">error</p>
// js代码
window.onerror = function (mesage, url, line) {
console.log(mes, url, line);
return true;
}
// message 错误信息
// url 错误发生的url
// line 错误发生行号
// return true 组织浏览器默认的报错行为.
- 图片加载失败,也可触发error事件,遵循DOM格式,返回event对象
const image = new Image();
image.addEventListener('load', event => {
console.log('image loaded');
})
image.addEventListener('error', (eve) => {
alert('图片加载失败....')
})
image.src = 'xxx.gif';
- 静态资源文件,如js,css的加载
// js代码
// 此处使用DOM2级绑定,才能监听错误
window.addEventListener('error', function (eve) {
console.log(eve, '滴滴,获取错误');
if (eve) {
let target = eve.target || eve.srcElement;
let isElementTarget = target instanceof HTMLElement;
if (!isElementTarget) {
// js错误
console.log('滴滴,js错误');
// 获取错误信息
let { filename, message, lineno, colno, error } = e;
let { message: ErrorMsg, stack } = error;
} else {
// 页面静态资源加载错误处理
console.log('滴滴,静态文件错误');
let { type, timeStamp, target } = eve;
let { localName, outerHTML, tagName, src } = target;
let typeName = target.localName;
let sourceUrl = "";
if (typeName === "link") {
sourceUrl = target.href;
} else if (typeName === "script") {
sourceUrl = target.src;
}
confirm('资源加载失败,换个网络或者刷新页面(' + sourceUrl + ')')
}
}
// 设为true表示捕获阶段调用,
return true;
}, true);
// html代码
<script type="text/javascript" src="a.js"></script>
<script type="text/javascript" src="b.js"></script>
一定注意要把js文件的引入放到下方,测试否则无效.
- 公司中使用 监控系统,推荐sentry
四 项目中的使用
1. 记录错误
- web项目中,经常建立错误日志存储和跟踪系统,也可记录js错误.
- 需要在服务器上有处理js错误的相关程序,该程序只用从查询字符串中获取数据,保存到日志中即可.
function logError(sev,msg){
let img = new Image(),
encodedSev = encodeURIComponent(sev),
encodedMsg = encodeURIComponent(msg);
img.src = `log.php?sev=${encodedSev}&msg=${encodedMsg}`
}
// sev 严重程度 msg 错误信息
// 使用img对象发送请求,不收跨域的限制,且也比较少的出错.
- 用到 try/catch的地方,都可以记录
for(let mod of mods){
try{
mod.init();
}catch(ex){
logError('nonfatal','Module init failed:${ex.message}')
}
}
2. 错误抛出的使用
-
抛出错误,可以方便我们一眼看出代码问题,可以减少额外的工作量.
-
大型网站中,通常用assert() 函数抛出错误.
// 抛出错误的函数
function assert(condition, message) {
if (!condition) {
throw new Error(message)
}
}
// 如果类型不符合要求则直接抛出错误
function divide(num1, num2) {
assert(typeof num1 == 'number' && typeof num2 == 'number', 'divide():Both arguments must be numbers.');
return num1 / num2;
}
console.log(divide(12, 'af'));
最后
- try/catch,可以通过更合适的方式处理错误,避免浏览器处理.
- 所有没有被try/catch处理的错误都会被window.onerror事件处理.更多的判断静态文件,是否加载成功.
别跑,据说给我一键三联的人写代码都没Bug! 您的支持就是我最大的动力!
我是飞翔,
愿您日有所长.