百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

第54节 错误处理及调试-Web前端开发之Javascript-零点程序-王唯

myzbx 2025-01-09 14:48 13 浏览

本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。

错误处理对于web应用开发至关重要,任何javascript错误都有可能会导致网页无法使用,因此作为开发人员,必须要及时处理有可能出现的错误;

从IE4.0之后,几乎所有的浏览器都包含了一些基本的错误处理功能,但并没有统一,后来,由ECMAscript添加了异常处理机制,也就是try…catch…finally结构以及throw操作;

错误处理的重要性: 好的错误处理技术可以让脚本的开发、调试和部署更加流畅,能对代码进行更好的控制;另外,JS缺乏标准的开发环境;

错误类型:

语法错误(syntax error):也称为解析错误,发生在传统编程语言的编译解释时;发生语法错误时,就会发生阻塞,也就是不能继续执行代码,但只是同一个线程中的代码会受影响,其他线程中的代码不受影响;

// Uncaught SyntaxError: Invalid or unexpected token
// 未捕获的语法错误:无效或意外的标记
document.write("zeronetwork;

运行时错误(Runtime error):也称为exception异常, 其发生在编译期/解释期后,此时,问题并不出现在代码的语法上,而是在尝试完成一个非法的操作;

<input type="button" value="单击" onclick="handleClick()" />
<script>
// Uncaught ReferenceError: openMy is not defined
// 未捕获的引用错误:未定义openMy
function handleClick(){
    openMy();
}
</script>

错误报告:

因为每个浏览器都有自己的内置Javascript解释程序,所以每种浏览器报告错误的方式都不同;有些是弹出错误信息,有些是把信息打印在控制台中;

IE(windows): 默认情况下,会弹出包含错误细节的对话框,并询问是否继续执行页面上的脚本;如果浏览器有调试器(如:Microsoft Script Debugger) ,此对话框会提供一个是调试还是忽略的选项;如果在IE设置中取消了”显示错误”,那么会在页面左下角显示一个黄色的图标;

注:如果JS代码就在HTML里,显示错误行是正确的,如果是外部的JS文件,则行号往往差一行,如第5行则为第4行;

Mozilla(所有平台): 在控制台中打印错误信息,并发出警告;其会报告三种类型的消息:错误、严格警告和消息等的;

Safari (MacOS):是对JavaScript错误和调试的支持最差,默认情况下,它对终端用户不提供任何javascript错误报告;

错误处理:

Javascript提供了两种处理错误的方式:

  • BOM包含一个onerror事件处理函数,该函数通常被绑定在window对象或image对象上;
  • ECMAscript定义了try…catch结构来处理异常;

onerror事件处理函数:

window对象的onerror属性是一个事件处理程序,页面上出现异常时,error事件便在window对象上触发,并把错误消息输出到Javascript控制台上,这种方式也称为全局错误捕获;如:

window.onload = function(){
    show(); // 在onload事件中调用了一个不存在的函数
}
window.onerror = function(){
    alert("出现错误");
    return true;
}

获取错误信息:

window.onerror事件处理程序在调用时可以传5个参数,由这5个参数可以获取详细的错误信息;

  • message:错误信息,描述错误的一条消息;
  • URL:引发错误的Javascript所在的文档的URL;
  • line:文档中发生错误的行数;
  • column:发生错误的列数;
  • error:错误对象,这个error也称为全局错误对象;
window.onerror = function(sMessage, sUrl, sLine, sColumn, error){
console.log("Error:" + sMessage + " URL:" + sUrl + " Line:" + sLine + " Column:" + sColumn);
console.log(error);
    return true;
}

onerror处理程序的返回值:

如果返回true,则阻止执行默认的事件处理程序,也就是将通知浏览器,事件处理程序已经处理了错误,不需要其他操作,反之会显示错误消息;

某些元素也支持onerror; 但其处理函数没有任何关于error信息的参数,如:

document.images[0].onerror = function(event){
console.log(event);  // Event
console.log(event.type);  // error
}

这里的event参数是一个类型为Event事件对象,其存储的信息除了type返回了error,并没有其他和错误相关的信息;

全局错误处理window.onerror通常不能恢复脚本继续执行,但会给开发者发送错误信息;

window.onerror = function(error){
  console.log(error);
}
show();
console.log("中止,不会被执行");
window.onload = function(){
  console.log("也不会被执行");
}

可以是简单的打印,也可以把错误保存到日志记录里;

window.onerror就是绑定在window对象的error事件,也可以使用标准的添加事件侦听的方式window.addEventListener(eventtype, handler),其需要两个参数,eventtype为事件类型,在此为error,handler是事件处理函数,其需要一个参数event,一个ErrorEvent类型的对象,其保存着有关事件和错误的所有信息,如:

window.addEventListener("error", function(event){
  console.log(event);  // ErrorEvent
  console.log(event.error);  // Error对象
  console.log(event.error.name);
  console.log(event.error.message);
  console.log(event.error.stack);
  console.log(event.lineno);  // 行
  console.log(event.colno);  // 列
  console.log(event.filename);
});

在实际的开发中,这两种方式都会被使用,只不过addEventListener有定的兼容必问题,所以要兼顾所有的浏览器且不太关注事件对象本身的话,就使用window.onerror;

当加载自不同域的脚本中发生语法错误时,为避免信息泄露,语法错误的细节将不会报告,只会返回简单的"Script error.";

<script>
window.onerror = function(msg, url, lineNo, columnNo, error){
  console.log(msg);  // Script error
  console.log(url);  // ""
  console.log(lineNo);  // 0
  console.log(columnNo);  // 0
  console.log(error);  // null
}
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>

可以针对同域和不同域的错误分开处理,如:

<script>
window.onerror = function(msg, url, lineNo, columnNo, error){
  var str_error = msg.toLowerCase();
  var sub_string = "script error";
  if(str_error.indexOf(sub_string) > -1)
    alert("脚本发生错误,详情请在控制台查看");
  else{
    var message = [
      '消息:' + msg,
      'URL:' + url,
      '行:' + lineNo,
      '列:' + columnNo,
      '错误对象:' + error
    ].join(" - ");
    alert(message);
  }
}
show();
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>

从上在的执行结果来看,error事件执行了两次,原因是使用了两个script,也就是当一个script有错误发生时,它只会阻止当前的script块,而不会阻止其他的script块;如:

<script>
show();  // 会捕获
console.log("不会被执行");
myshow();  // 不会捕获
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>
<script>
console.log("执行了");
demo();  // 会捕获
console.log("不会被执行");
</script>

body元素的onerror特性,也可以充当事件处理函数,如:

<body onerror="alert('出现了错误');return true;">

注意,先注释掉window.onerror等代码;

此时,可以直接使用event、source、lineno、colno、error等属性;

<body onerror="alert(event + '\n' + source + '\n' + lineno + '\n' + colno + '\n' + error);return true;">

当然了,也可以为body绑定error事件,此时各属性,必须明确指定,如:

document.body.onerror = function(msg, url,lineno,colno,error){
  alert(msg + '\n' + url + '\n' + lineno + '\n' + colno + '\n' + error);
  return true;
}

try-catch语句:

try语句中为期待正常执行的代码块,当在try语句中发生错误,其余代码会中止执行,catch语句就处理该错误,如果没有错误,就跳过catch语句;try和catch必须成对出现;

try{
    //code
    [break]
}catch([exception]){
    //code
}[finally]{
    //code
}
// 如
try {
    show();
    alert("不能执行");
} catch (error) {
    alert("出现一个错误:" + error);
} finally{
    alert("管你呢");
}

try语句块内的错误只会中止try语句块中发生错误之后的逻辑代码,并不会中止整个脚本的运行;执行try-catch语句,必须是运行时,运行时错误,也被称为异常;try-catch语句中指定只能有一个catch子句;try-catch语句适合处理无法预知、无法控制的错误;finally常被用于无论结果是否有异常,都要执行的代码,如:

try{
  alert("try");
  show();
  alert("no exec");
}catch(error){
  alert("catch");
}finally{
  alert("finally");
}
alert("continute");

代码执行的两条路径:如果没有异常,执行路径为:try->finally,反之为:try的部分->catch->finally;

一般用于关闭打开的链接和释放资源;

var connection = {open: function(){},close: function(){},send: function(data){}}
// var data = "大师哥王唯"; // 注释这一行,让它产生异常
connection.open();
try{
    connection.send(data);
}catch(exception){
    console.log("出现一个错误");
}finally{
    connection.close();
    console.log("关闭了");
}

还有一个典型的应用,读写文件,如:

function openFile(){};
function writeFile(data){};
function closeFile(){};
openFile();
try{
  writeFile();
}catch(error){
  console.log(error);
}finally{
  closeFile();
}

在try-catch-finally语句块中的变量是全局变量:

try{
  var name = "王唯";
  show();
  var city = "蚌埠";
}catch(error){
  var age = 18;
  console.log(name);
}finally{
  var sex = "男";
  console.log(name);
  console.log(age);
}
console.log(name);
console.log(city);
console.log(age);
console.log(sex);

try-catch-finally与return:

如果直接在try-catch-finally语句块中执行return,会抛出异常,如:

try {
  console.log("try");
  // return;  // Illegal return statement 非法返回语句
  console.log("try agin");
} catch (error) {
  console.log(error);
  // return;  // Illegal return statement
}finally{
  console.log("finally");
  // return;  // Illegal return statement
}

如:

function foo(){
  try {
    console.log("try");
    return 1;
    show();
    console.log("try agin");
  } catch (error) {
    console.log(error);
    return 2;
  }finally{
    console.log("finally");
    return 3;
  }
}
console.log(foo());  // 3

try-finally:

没有catch从句,只有try-finally也可以,目的是,只确保执行开始和最终的过程,而不处理错误,如:

try{
  console.log("try");
  show();
}finally{
  console.log("finally"); // 会执行
}
console.log("over"); // 不会执行,已中止

但此时,还是会抛出异常的,但此时,会在执行完finally后中止执行,并会查找外部的catch语句;

嵌套try-catch语句:

在catch子句中,也有可能会发生错误,所以就可以使用嵌套的try-catch语句,如:

try {
    show();
    console.log("不能执行");
} catch (error) {
    console.log("出现一个错误:" + error);
    try {
        var arr = new Array(10000000000000000);
        arr.push(error);
    } catch (error) {
        console.log("又出现了一个错误:" + error);
    }
} finally{
    console.log("管你呢");
}

也可以在try中嵌套try-catch-finally语句,如:

try{
  try{
    console.log("try");
    show();
  }catch(error){
    console.log("error");
  }finally{
    console.log("finally");
  }
}catch(error){
  console.log(error);
}

一个比较典型的应用,就是处理json数据,如:

// var json = '{"name":"wangwei", "age": 18, "sex": "男"}';
var json = '{bad json}';  // Uncaught SyntaxError
var data = JSON.parse(json);
console.log(data.name);
console.log(data.age);
console.log(data.sex);

一量json数据发生错误,整个应用都会崩溃,所以应该使用try-catch,如:

<div id="msg">您的信息:</div>
<script>
window.onload = function(){
  var msg = document.getElementById("msg");
  try{
    // var json = '{"name":"王唯", "age": 18, "sex": "男"}';
    var json = '{bad json}';  // Uncaught SyntaxError
    var data = JSON.parse(json);
    msg.innerHTML += "姓名:" + data.name + ",年龄:" + data.age + ",性别:" + data.sex;
  }catch(error){
    msg.innerHTML = "开小差了,找不到你的信息";
  }
}
</script>

当使用了try-catch语句,就不会将错误提交给浏览器,也就不会触发error事件,如:

window.onerror = function(error){
  console.log(error);  // 不会触发
}
try{
  show();
}catch(error){
  console.log(error);
}

Error错误对象:

在catch中会捕获一个Error错误对象;该对象在Javascript解析或运行时,一旦发生错误,引擎就会抛出这个对象;如果没有相关联的try-catch捕获该对象,就由浏览器输出这个对象;

// ...
console.log("错误:" + error + " name:" + error.name + " message:" + error.message);

也可以通过Error的构造器创建一个错误对象,这个Error对象也可用于用户自定义的异常;语法:new Error([message[, filename[, lineNumber]]]);

  • message:可选,错误描述信息;
  • fileName:可选,非标准,被创建的Error对象的fileName属性值,默认是调用Error构造器代码所在的文件的名字; 但大部分浏览器没有实现;
  • lineNumber:可选,非标准,被创建的Error对象的lineNumber属性值,默认是调用Error构造器代码所在的文件的行号;但大部分浏览器没有实现;

实例化Error对象,也可以不使用new关键字,如:

var error = new Error("自定义错误对象");
var error = Error("不使用new");
console.log(error);
console.log(error.name);  // Error
console.log(error.message);  // 自定义错误对象

Error错误对象属性:

  • name:表示错误类型的字符串;
  • message:实际的错误信息;

Error类还有6个子类,其可以通过错误对象的name属性返回具体异常类的名称:

  • EvalError:错误发生在eval()函数中;
  • RangeError:数值超出javascript可表示的范围;;
  • ReferenceError:使用了非法或不能识别的引用;
  • SyntaxError:发生了语法错误;
  • TypeError:操作数的类型不是预期所需的;
  • URIError:在encodeURI()或decodeURI()函数中发生了错误;
// EvalError
try{
  throw new EvalError("Eval异常");
}catch(error){
  console.log(error);
  console.log(error instanceof EvalError);  // true
  console.log(error.name);  // EvalError
  console.log(error.message);  // Eval异常
}
// RangeError
var num = 1;
try{
  num.toPrecision(500);  //  [pr??s??n] 精度
}catch(error){
  console.log(error); 
}
// ReferenceError
var x;
try {
  x = y + 1;
} catch (error) {
  console.log(error);
}
// SyntaxError
try{
  eval("alert('wangwei)");
}catch(error){
  console.log(error);
}
// TypeError
var num = 1;
try{
  num.toUpperCase(); // 无法将数字转为大写
}catch(error){
  console.log(error);
}
// URIError (malformed [?m?l?f??md]格式不正确,畸形的)
try{
  decodeURI("%%%"); // 使用了非法字符
}catch(error){
  console.log(error);
}

Error对象的message属性是浏览器生成的用于表示错误描述的信息,因为这个属性是特定于浏览器的,所以不同的浏览器上可能产生不同的错误信息,如:

try {
    eval("a ++ b");
    show();
    console.log("执行了吗?");
} catch (error) {
    // SyntaxError:Unexpected identifier或
    // SyntaxError:unexpected token: identifier
    console.log(error.name + ":" + error.message);
}

使用name属性判断错误类型:

try {
    eval("a++b");
} catch (error) {
    console.log(error instanceof SyntaxError); // true
    if(error.name == "SyntaxError")
        console.log(error.name + ":" + error.message);
    else
        console.log("未知错误:" + error.message);
}

抛出异常:

throw语句的作用是手动中断程序执行,抛出一个错误,一般用于有目的的抛出异常,也就是允许开发者可以创建自定义错误;

throw可以抛出任何类型的值,也就是说,它的参数可以是任何值;语法:throw error_object;error_object可以是字符串、数字、布尔或对象;

throw "出现一个错误";
throw 50666;
throw true;
throw new Object();
throw {
    toString:function(){
        return 'Error!';
    }
}
function getRectArea(width, height){
  if(isNaN(width) || isNaN(height))
    throw '参数应该是number类型';
  return width * height;
}
getRectArea("wangwei",10);

对于Javascript引擎来说,只要遇到throw语句,程序就会终止;

也可以抛出一个Error错误对象;Error对象的构造函数只有一个参数,

throw new Error("请再次尝试");

其他Error子类对象也可以抛出:

throw new SyntaxError("...");
throw new TypeError("...");
throw new RangeError("...");
throw new EvalError("...");
throw new URIError("...");
throw new ReferenceError("...");

对于Error类和其子类来说,错误对象的name就是其构造函数的名称,message是其构造函数的参数;

当抛出异常后,throw之后的语句将不会执行,并跳到相关联的catch语句中进行处理,如:

<h1>请输入18-99之间的数字</h1>
<input id="txtInput" type="text" />
<button id="btn">确定</button>
<p id="msg"></p>
<script>
function myFun(){
  var msg,x;
  msg = document.getElementById("msg");
  msg.innerHTML = "";
  x = document.getElementById("txtInput").value;
  try{
    if(x == "") throw "空的";
    if(isNaN(x)) throw "不是数字";
    x = Number(x);
    if(x < 18) throw "太小";
    if(x > 99) throw "太大";
    msg.innerHTML = "输入的值正确:" + String(x);
  }
  catch(error){
    msg.innerHTML = "输入的值不正确:" + error; 
  }
}
var btn = document.getElementById("btn");
btn.onclick = myFun;
</script>

也可以在某个语句块的外部捕获throw异常,如:

function sum(a,b){
    if(arguments.length < 2)
        throw new Error("需要两个参数");
    else
        return a + b;
}
try{
    console.log(sum(18));
}catch(error){
    // Error:需要两个参数
    console.log(error.name + ":" + error.message);
}

可以通过instanceof判断异常的类型来特定处理某一类的异常,例如可以区分浏览器抛出的异常和开发人员抛出的异常,如:

function sum(a,b){
    if(arguments.length < 2)
      throw new Error("需要两个参数");
    if(isNaN(a) || isNaN(b))
      throw "参数是不是Number类型";
    return a + b;
}
try{
    console.log(sum(18,12));
}catch(error){
  if(error instanceof SyntaxError)
    console.log("语法错误:" + error.name + ":" + error.message);
  else if(error instanceof Error)
    console.log(error.name + ":" + error.message);
  else
      console.log(error);
}

注:判断Error类型要放到if的最后一个条件;

即使在catch语句中,还可以根据实际情况,再次抛出异常,此时,其可以被外部的try-catch语句块捕获(如果存在的话);

当发生异常时,代码会立即停止,仅当有try-catch语句捕获到异常时,代码才会继续执行;其背后运行的原理是,当发生异常,JavaScript解释器会立即停止执行的逻辑,并跳转到就近的try-catch异常处理程序,如果发生异常的代码块中没有相关联的catch从句,解释器会检查更高层的闭合代码块,看它是否有相关联的异常处理程序,以此类推,直到找到一个异常处理程序为止;如果发生异常的函数中没有处理它的try-catch语句,异常将向上传播到调用该函数的代码,如此,异常就会沿着Javascript的语法结构或调用栈向上传播;如果没有找到任何异常处理程序,JavaScript将把异常当成程序错误来处理,并通过浏览器报告给用户;

自定义错误类型:

可以基于Error类来创建自定义的错误类型,此时可以使用throw抛出自定义的异常类,或通过instanceof来检查这个异常类的类型,新类型需要实现name和message属性;

function CustomError(message){
  this.name = "CustomError";
  this.message = message || 'Default Message';
  this.stack = (new Error()).stack;
}
// CustomError.prototype = new Error();
// 或者
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;
try{
  var name = "jingjing";
  if(name !== "wangwei")
    throw new CustomError("自定义的错误类型");
}catch(error){
  console.log(error.message);
}

小示例:

function UserException(message){
  this.name = "UserException";
  this.message = message;
}
function getMothName(m){
  m = m - 1;  // 调整月份数字到数组索引(1=Jan,12=Dec)
  var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  if(months[m] != undefined)
    return months[m];
  else
    throw new UserException("Invalid Month No");
}
try{
  var myMonth = 15;
  var monthName = getMothName(myMonth);
}catch(error){
  var monthName = "未知";
  console.log(error.name + ":" + error.message);
}

小示例:验证电话号码,如:

function Telephone(num){
  num = String(num);
  var pattern = /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/;
  if(pattern.test(num)){
    this.value = num.match(pattern)[0];
    this.valueOf = function(){
      return this.value;
    };
    this.toString = function(){
      return String(this.value);
    }
  }else{
    throw new TelephoneFormatException(num);
  }
}
function TelephoneFormatException(value){
  this.name = "TelephoneFormatException";
  this.message = "电话号码格式不正确";
  this.value = value;
  this.toString = function(){
    return this.value + ":" + this.message;
  }
}
// 应用
var TELEPHONE_INVALID = -1;
var TELEPHONE_UNKNOWN_ERROR = -2;
function verifyTelephone(num){
  try{
    num = new Telephone(num);
  }catch(error){
    if(error instanceof TelephoneFormatException)
      return TELEPHONE_INVALID;
    else
      return TELEPHONE_UNKNOWN_ERROR;
  }
  return num.toString();
}
console.log(verifyTelephone("010-66668888"));
console.log(verifyTelephone("13812345678"));
console.log(verifyTelephone("138123456")); // -1
console.log(verifyTelephone("wangwei")); // -1

常见错误:

由于javaScript是松散类型的,也不会验证函数的参数,因此错误只会在运行时出现;一般来说,需要关注三种错误:类型转换错误、数据类型错误、通信错误;

类型转换错误:

一般发生在使用某个操作符,或者使用其他可能自动转换值的数据类型的语言结构时;

function output(str1,str2,str3){
  var result = str1 + str2;
  if(str3)
    result += str3;
  return result;
}
console.log(output(1,2,3));
console.log(output(1,2));
console.log(output(1,2,0));
console.log(output(1,2,"wangwei"));

这就是一个非常典型的与期望不一致的方式;

数据类型错误:

在流控制语句中使用非布尔值,是极为常见的一个错误来源,为避免此类错误,就要做到在条件比较时确定传入的是布尔值,例如,把if语句改成:if(typeof str3 == 'number');

所以在使用某个变量或对象时,一定要适当地检查它的数据类型,如:

function reverseSort(values){
  // if(values){
  //   values.sort();
  //   values.reverse();
  // }
  if(arguments.length > 0){
    if(!Array.isArray(values))
      return [];
    else{
      values.sort();
      values.reverse();
      return values;
    }
  }
  return [];
}
var arr = [3,2,6,9,4];
// var arr = 100;  // Uncaught TypeError: values.sort is not a function
console.log(reverseSort(arr));

另一个常见的错误就是将参数与null值进行比较。与null进行比较只能确保相应的值不是null和undefined。要确保传入的值有效,仅检测null值是不够的;

function reverseSort(values){
  // if(values != null){  // 任何非数组值都会导致错误
  if(values instanceof Array) // 非数组值被忽略
    values.sort();
    values.reverse();
  }
  return values;
}
var arr = [3,2,6,9,4];
// var arr = 100;  // Uncaught TypeError: values.sort is not a function
console.log(reverseSort(arr));
// 或
function reverseSort(values, fun){
  if(values instanceof Array){
    if(fun != null && typeof fun === "function")
      values.sort(fun);
    else
      values.sort();
  }
  return values;
}
var arr = [3,2,6,9,4];
console.log(reverseSort(arr, function(a,b){
  return a > b ? -1 : 1;
}));

通信错误:最典型的就是Ajax应用,用其可以动态加载信息,但是,javascript与服务器之间的任何一次通信,都有可能会产生错误;

调试技巧:

使用警告框: 这是最简单、流行的方式,如:

function test(){
    alert("函数内");
    var iNum1 = 5, iNum2 = 10;
    alert(iNum1);
    var iResult = iNum1 + iNum2;
    alert(iResult);
}
test();

抛出自定义错误:

function assert(bCondition, sErrorMessage){
    if(!bCondition)
        throw new Error(sErrorMessage);
}
function divide(iNum1, iNum2){
    assert(arguments.length == 2, "divide需要两个参数");
    assert((!isNaN(iNum1) && !isNaN(iNum2)), "需要Number类型");
    return iNum1 / iNum2;
}
console.log(divide(10,2));
console.log(divide(10,"c"));  // 异常
// 或
try{
  console.log(divide(10,"c"));
}catch(error){
  console.log(error.name + ":" + error.message);
}

Javascript校验器:

jslint的主要目的是指出不合规范的js语法和可能的语法错误,包括一些不良代码;官网:http://www.jslint.com/

如以下,会给出警告:

  • 语句未使用块标记;
  • 一行的结尾未以分号结束;
  • 用var声明一个已在使用的变量;
  • with语句;

调试器:

Javascript自身不具备调试器,但目前所有的浏览器可以使用自身的调试器;

IE调试:

启用IE的调试功能:

菜单“工具”|“Internet选项”命令,打开“Internet选项”对话框,在“高级”选项卡中,找到两个“禁用脚本调试”复选框并取消;开始调试,调试的主要工作是反复地跟踪代码,找出错误并修正;

设置断点:

在调试程序窗口中,将光标移动到需要添加断点的行上,按一次F9键或单击,当前行的背景色变为红色,并且在窗口左边界上标上红色的圆点,当程序运行到断点时就会暂停;

运行调试:

单击继续或按F5进行逐步运行调试;F10步进、F11步入,都可以继续向下执行;将鼠标移动到变量名上时,会显示变量当前时刻的值;或者在右侧的“监视”窗格中可以观察该变量的值;点击变量信息框中的变量值或右侧“监视”空格中的变量值可以修改变量的当前值;更多的调试操作:查看调用关系、监视特定变量的值等;

// 示例
var balance = 200.0;    //
var willPay = 20.0;
function pay(_balance, _pay){
    return _balance - _pay;
}
function showBalance(){
    debugger;
    var blnc = pay(balance,willPay);
    alert("当前余额:" + blnc);
}
showBalance();

日志输出:

程序运行时,有些中间数据需要记录,以便检查程序运行的状态;对于JavaScript记录中间数据通常是以日志的形式记录需要记录的数据,再发送到服务器上保存起来;日志记录的内容可以是任意的信息,根据开发者的需要而定;

function Logger(){          // 日志对象构造函数
    function record(_serial, _message){ // 记录对象构造函数
        this.serial = _serial;          // 记录编号
        this.message = _message;        // 记录信息
        this.date = new Date();         // 记录时间
    }
    this.recordList = new Array();      // 创建数组容器
    this.index = 0;                     // 记录索引
    this.log = function(info){          // “添加日志”函数
        var newLog = new record(++this.index, info);
        this.recordList.push(newLog);
    }
    this.showLog = function(_mode){     // 显示记录信息
        var info = "";  // 分析出的日志信息文本
        for(n in this.recordList){      // 逐一分析记录数组
    if(_mode == 0){ // 显示模式0
        info += "<li>" + this.recordList[n].serial + "("
        + this.recordList[n].date.toLocaleString() + "):"
        + this.recordList[n].message + "</li>";// 格式化信息
            if(n == (this.recordList.length - 1)){
            document.write(info);   // 在当前文档输出
                }
            }else if(_mode == 1){   // 显示模式1
            info += "#" + this.recordList[n].serial + "("
            + this.recordList[n].date.toLocaleString() + "):"
                + this.recordList[n].message + "\n";    // 格式化信息
            if(n == (this.recordList.length - 1)){
                alert(info);    // 以对话框的形式输出
            }
        }
        }
        return info;        // 将格式化后的信息返回给调用
    }
}
// 应用
var g_log = new Logger();   // 创建一个全局日志对象
var balance = 200.0;        // 当余额
g_log.log( "balance:" + balance );  // 上一步操作后,添加日志
var willPay = 20.0;     // 当前该付金额
g_log.log( "willPay:" + willPay ); 
function pay( _balance, _pay ){ // 付账动作
    g_log.log( "_balance:" + _balance );
    g_log.log( "_pay:" + _pay ); 
    return _balance - _pay;     // 从余额中减去该付的数额
}
function ShowBalance(){         
    var blnc = pay( balance, willPay ); // 付账
    g_log.log( "blnc:" + blnc );
    document.write( "当前余额:" + blnc );   // 输出余额
}
ShowBalance();      // 显示余额
g_log.showLog(0);       // 输出日志信息

相关推荐

攀升战境S5电竞主机评测:NVIDIA RTX 3060实力助阵,光追游戏走起

此次笔者将为玩家们推荐一款游戏主机——攀升战境S5。该主机是攀升电脑今年力推的游戏装备,主机采用一线品牌配件,特别是在显卡选用上严苛把关,精选GeForceRTX30系列显卡,玩家们大可以放心选购...

慎买-神牛闪光灯兼容性问题:神牛V350&amp;松下S5M2

神牛V350和松下S5M2的兼容性问题。大家好,我是向往闪光灯人像的Fish。国庆期间,我购买了神牛V350闪光灯和神牛X2T引闪器,但这成为了我的噩梦。我原以为客服和松友们说这款闪光灯在松下S5M2...

Acer蜂鸟持续办公一整天(acer 蜂鸟s5)

移动办公在工作节奏日益加快的今天越来越普遍,目前大部分工作无法在手持设备上完成,笔记本依然是移动办公最明智的选择。为了实现移动办公,很多笔记本越做越轻薄,性能也越来越强,而续航却一直没有很大提升。笔者...

职业车手明年会骑什么?2021赛季各大世巡赛车队使用器材一览

新年的钟声即将敲响,意味着充满魔幻色彩的2020年即将过去。受新冠肺炎的影响,2020年的赛季非常不同寻常。因这一原因不得不延迟举行的各种比赛导致许多车队的赞助商无法得到足够曝光,这也间接导致了许多车...

三星部分手机系统升级路线图流出(三星系统在哪升级)

三星包括Note3和S5在内的手机在升级到4.4.2系统之后一直没有什么系统升级的消息,而最近流出的一张三星的系统升级路线图中出现了一共13台手机升级KTU84P(也就是Android4.4.4)...

索尼Xperia Z3配置大曝光:升级并不大

IT之家(www.ithome.com):索尼XperiaZ3配置大曝光:升级并不大索尼明天就会在IFA2014大会上发布其下代旗舰XperiaZ3智能手机,目前网上曝光了其原型机,并且机身背后...

不进反退 三星Exynos 5433只能运行32位模式?

三星GalaxyNote4将带有两个版本,除了国行使用的骁龙805以外,还有三星自家的Exynos5433版本。而这颗SoC的详细信息三星并没有公布,据外媒Anandtech称,他们从源码中确认...

尼康Z6III测评:对比EOS R6 II、A7M4、S5IIX

摄影器材测评网站DPReview刚刚发布了尼康Z6III的完整图文测评,该机获得金奖评级,得分达到91%。以下是该文章的摘录——尼康Z6III核心规格:2400万像素“部分堆栈式”传感器RAW连拍:机...

赛默飞Ion S5首批数据公布,玩爆前任PGMTM系列

北美时间9月1日,赛默飞发布了两款最新的NGS系统IonS5和IonS5XL,旨在提供更加简捷的靶向测序流程。10月29日IonS5测序仪的首批实验数据产生于阜外医院。阜外医院研究人员选用了主...

Excel技巧:快速制作批量文件夹,省时省力,加强工作效率

大家好,如果公司领导要求按人员姓名制作文件夹,以一人一档的形式呈现人员档案,办公人员一个一个制作费时费力,而且效力低下,今天为大家介绍快捷制作批量文件夹的方法下面我们用图片来进行演示操作打开表格,选...

国行、港版、美版Apple watch各版本售价一览

今天凌晨,苹果牌手表正式发布,苹果开始正式进入可穿戴设备领域,除了功能和外观,我相信大家更关心的是价格问题了,小编就将国行、港版、美版的Applewatch售价做一总结,以供参考。国行:美版:港版:...

松下全画幅微单S5和S1到底哪里不一样?

Hello,我是ET,欢迎大家来到我的“相机笔记”。————9月2日晚,松下正式发布了第4款全画幅微单LUMIXS5。这一篇,我们主要来说松下LUMIXS5和LUMIXS1到底有哪些区别...

融会贯通之典范 神舟S7-2021S5评测

便携、性能、续航,这简简单单的六个字道出了这么些年来笔记本电脑的设计方向,可是由于底层技术、模具设计等等原因,这三点并不能很好的融合在一起。虽说闻道有先后,术业有专攻,但能够有一台融会贯通的产品,不是...

三国志战略版:S5赛季装X指南,开荒不是一成不变,需要因地制宜

大家好我是零氪玩家花席,S5赛季已经开始,因为S5赛季的野地阵容和S4赛季没有区别,所以S5赛季开荒相对不难。你在S4有经验,并且多了很多武将和战法,还能用150赛季功勋兑换7500战法点。S5赛季新...

聊聊松下S5M2和S5M2X的区别(松下s5k和s5c有什么区别)

先简单说下哪里不同:12bitRAWHDMI外录支持直接将视频录制到USB-SSD上多了All-Intra和ProRes编码支持有线/无线IP推流,USB网络连接黑化的机身不过要特别强调一下,S5...