【javascript】错误处理和调试
2018-01-04 本文已影响3人
shanruopeng
1、错误处理
- 任何有影响力的Web应用程序都需要一套完善的错误处理机制,良好的错误处理机制可以让用户及时得到提醒。
1.1 try-catch语句
try{
// 可能会导致错误的代码
} catch(error){
// 在错误发生时怎么处理
}
(1)finally 子句
- 虽然在try-catch 语句中是可选的,但finally子句一经使用,其代码无论如何都会执行。
- 只要代码中包含finally子句,则无论try或catch语句块中包含什么代码——甚至return 语句,都不会阻止finally 子句的执行
function testFinally(){
try {
return 2;
} catch (error){
return 1;
} finally {
return 0;
}
}
/**调用这个函数只能返回0**/
(2)错误类型
-
执行代码期间可能会发生的错误有多种类型。每种错误都有对应的错误类型,而当错误发生时,就会抛出相应类型的错误对象。
- Error
- EvalError
- RangeError
- ReferenceError
- SyntaxError
- TypeError
- URIError
-
EvalError 类型的错误会在使用eval()函数而发生异常时被抛出。
new eval(); //抛出EvalError
eval = foo; //抛出EvalError
- RangeError 类型的错误会在数值超出相应范围时触发。
var items1 = new Array(-20); //抛出RangeError
var items2 = new Array(Number.MAX_VALUE); //抛出RangeError
- 在找不到对象的情况下,会发生ReferenceError。
var obj = x; //在x 并未声明的情况下抛出 ReferenceError
- 当我们把语法错误的JavaScript字符串传入eval()函数时,就会导致此SyntaxError。
eval("a ++ b"); //抛出SyntaxError
- 在执行特定于类型的操作时,变量的类型不符合要求,会导致TypeError。
var o = new 10; //抛出TypeError
alert("name" in true); //抛出TypeError
Function.prototype.toString.call("name"); //抛出TypeError
-
在使用encodeURI()或decodeURI(),而URI 格式不正确时,就会导致URIError 错误。
-
利用不同的错误类型,可以获悉更多有关异常的信息,从而有助于对错误作出恰当的处理。
try {
someFunction();
} catch (error){
if (error instanceof TypeError){
//处理类型错误
} else if (error instanceof ReferenceError){
//处理引用错误
} else {
//处理其他类型的错误
}
}
(3)合理使用try-catch
- 使用try-catch 最适合处理那些我们无法控制的错误。假设你在使用一个大型JavaScript 库中的函数,该函数可能会有意无意地抛出一些错误。由于我们不能修改这个库的源代码,所以大可将对该函数的调用放在try-catch语句当中,万一有什么错误发生,也好恰当地处理它们。
- 在明明白白地知道自己的代码会发生错误时,再使用try-catch 语句就不太合适了。
1.2 抛出错误
-
与try-catch 语句相配的还有一个throw 操作符,用于随时抛出自定义错误。
-
抛出错误时,必须要给throw 操作符指定一个值,这个值是什么类型,没有要求。
-
在遇到throw 操作符时,代码会立即停止执行。仅当有try-catch语句捕获到被抛出的值时,代码才会继续执行。
-
利用原型链还可以通过继承Error 来创建自定义错误类型。
function CustomError(message){
this.name = "CustomError";
this.message = message;
}
CustomError.prototype = new Error();
throw new CustomError("My message");
(1)抛出错误的时机
- 要针对函数为什么会执行失败给出更多信息,抛出自定义错误是一种很方便的方式。
function process(values){
if (!(values instanceof Array)){
throw new Error("process(): Argument must be an array.");
}
values.sort();
for (var i=0, len=values.length; i < len; i++){
if (values[i] > 100){
return values[i];
}
}
return -1;
}
(2) 抛出错误与使用try-catch
-
如果你打算编写一个要在很多应用程序中使用的JavaScript库,甚至只编写一个可能会在应用程序内部多个地方使用的辅助函数,我都强烈建议你在抛出错误时提供详尽的信息。然后,即可在应用程序中捕获并适当地处理这些错误。
-
捕获那些确切地知道该如何处理的错误。捕获错误的目的在于避免浏览器以默认方式处理它们;而抛出错误的目的在于提供错误发生具体原因的消息。
1.3 错误事件
- 在任何Web 浏览器中,onerror事件处理程序都不会创建event对象,但它可以接收三个参数:错误消息、错误所在的URL 和行号。
- 只要发生错误,无论是不是浏览器生成的,都会触发error事件,并执行这个事件处理程序。
window.onerror = function(message, url, line){
alert(message);
//阻止浏览器报告错误的默认行为。
return false;
};
1.4 常见的错误类型
- 由于JavaScript 是松散类型的,而且也不会验证函数的参数,因此错误只会在代码运行期间出现。一般来说,需要关注三种错误:
- 类型转换错误
- 数据类型错误
- 通信错误
(1)类型转换错误
- 建议使用全等(===)和不全等(!==)操作符,以避免类型转换。
- 容易发生类型转换错误的另一个地方,就是流控制语句。像if之类的语句在确定下一步操作之前,会自动把任何值转换成布尔值。尤其是if语句,如果使用不当,最容易出错。
function concat(str1, str2, str3){
var result = str1 + str2;
if (str3){ //绝对不要这样!!!
result += str3;
}
return result;
}
function concat(str1, str2, str3){
var result = str1 + str2;
if (typeof str3 == "string"){ //恰当的比较
result += str3;
}
return result;
}
(2) 数据类型错误
- 为了保证不会发生数据类型错误,只能依靠开发人员编写适当的数据类型检测代码。在将预料之外的值传递给函数的情况下,最容易发生数据类型错误。
/**例子一**/
//不安全的函数,任何非字符串值都会导致错误
function getQueryString(url){
var pos = url.indexOf("?");
if (pos > -1){
return url.substring(pos +1);
}
return "";
}
function getQueryString(url){
if (typeof url == "string"){ //通过检查类型确保安全
var pos = url.indexOf("?");
if (pos > -1){
return url.substring(pos +1);
}
}
return "";
}
/**例子二**/
//不安全的函数,任何非数组值都会导致错误
function reverseSort(values){
if (values){ //绝对不要这样!!!
values.sort();
values.reverse();
}
}
//不安全的函数,任何非数组值都会导致错误
function reverseSort(values){
if (values != null){ //绝对不要这样!!!
values.sort();
values.reverse();
}
}
//还是不安全,任何非数组值都会导致错误
function reverseSort(values){
if (typeof values.sort == "function"){ //绝对不要这样!!!
values.sort();
values.reverse();
}
}
//安全,非数组值将被忽略
function reverseSort(values){
if (values instanceof Array){ //问题解决了
values.sort();
values.reverse();
}
}
(3)通信错误
- 第一种通信错误与格式不正确的URL或发送的数据有关。最常见的问题是在将数据发送给服务器之前,没有使用encodeURIComponent()对数据进行编码。
- 对于查询字符串,应该记住必须要使用encodeURIComponent()方法。为了确保这一点,有时候可以定义一个处理查询字符串的函数。
function addQueryStringArg(url, name, value){
if (url.indexOf("?") == -1){
url += "?";
} else {
url += "&";
}
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
1.5 区分致命错误和非致命错误
- 对于非致命错误,可以根据下列一或多个条件来确定:
- 不影响用户的主要任务;
- 只影响页面的一部分;
- 可以恢复;
- 重复相同操作可以消除错误。
- 致命错误,可以通过以下一或多个条件来确定:
- 应用程序根本无法继续运行;
- 错误明显影响到了用户的主要操作;
- 会导致其他连带错误。
for (var i=0, len=mods.length; i < len; i++){
mods[i].init(); //可能会导致致命错误
}
for (var i=0, len=mods.length; i < len; i++){
try {
mods[i].init();
} catch (ex) {
//在这里处理错误
}
}
1.6 把错误记录到服务器
- 开发Web 应用程序过程中的一种常见的做法,就是集中保存错误日志,以便查找重要错误的原因。
- 要建立这样一种JavaScript错误记录系统,首先需要在服务器上创建一个页面用于处理错误数据。这个页面的作用无非就是从查询字符串中取得数据,然后再将数据写入错
误日志中。 - 这个页面可能会使用如下所示的函数:
function logError(sev, msg){
var img = new Image();
img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" +
encodeURIComponent(msg);
}
- 使用了Image 对象来发送请求,这样做非常灵活,主要表现如下几方面。
(1)所有浏览器都支持Image 对象,包括那些不支持XMLHttpRequest 对象的浏览器。
(2)可以避免跨域限制。通常都是一台服务器要负责处理多台服务器的错误,而这种情况下使用XMLHttpRequest 是不行的。
(3)在记录错误的过程中出问题的概率比较低。
for (var i=0, len=mods.length; i < len; i++){
try {
mods[i].init();
} catch (ex){
logError("nonfatal", "Module init failed: " + ex.message);
}
}
2、调试技术
(1)将消息记录到控制台
- 可以通过console 对象向JavaScript 控制台中写入消息。
- error(message):将错误消息记录到控制台
- info(message):将信息性消息记录到控制台
- log(message):将一般消息记录到控制台
- warn(message):将警告消息记录到控制台
(2)将消息记录到当前页面
- 在页面中开辟一小块区域,用以显示消息
(3)抛出错误
function divide(num1, num2){
if (typeof num1 != "number" || typeof num2 != "number"){
throw new Error("divide(): Both arguments must be numbers.");
}
return num1 / num2;
}
- 对于大型应用程序来说,自定义的错误通常都使用assert()函数抛出。这个函数接受两个参数,一个是求值结果应该为true的条件,另一个是条件为false时要抛出的错误。
//基本的assert()函数
function assert(condition, message){
if (!condition){
throw new Error(message);
}
}
- 使用assert()函数可以减少抛出错误所需的代码量.
function divide(num1, num2){
assert(typeof num1 == "number" && typeof num2 == "number",
"divide(): Both arguments must be numbers.");
return num1 / num2;
}
好好学习