65.9K
CodeProject 正在变化。 阅读更多。
Home

如何使用 Error 更快地诊断 JavaScript 错误

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (1投票)

2013 年 2 月 13 日

CPOL

6分钟阅读

viewsIcon

16321

这些应用程序日益强大和复杂,意味着开发人员需要更好的工具,例如 Error.stack,来处理错误和诊断 bug。在本文中,我将向您展示一些简单的调试技巧,以帮助您节省时间。

30 天开发一个 Windows 8 应用

现代浏览器(如 Internet Explorer 10)支持 Error.stack,它使 Web 开发人员能够更快地诊断和纠正 bug,特别是那些难以重现的 bug。开发人员可以利用当今现代浏览器所驱动的 Web 平台的功能构建出色的应用程序,也可以构建 Windows 8 中的应用程序。这些应用程序日益强大和复杂,意味着开发人员需要更好的工具,例如 Error.stack,来处理错误和诊断 bug。在本文中,我将向您展示一些简单的调试技巧,以帮助您节省时间。

调试应用程序

JavaScript 中的结构化错误处理依赖于 throw 和 try/catch – 开发人员声明一个错误,并将控制流传递给程序中处理错误的部分。当抛出错误时,Internet Explorer 中的 JavaScript 引擎 Chakra 会捕获导致错误源头的调用链 – 也称为调用堆栈。如果抛出的对象是 Error(或者是一个原型链回溯到 Error 的函数),Chakra 会创建一个堆栈跟踪,这是一个人类可读的调用堆栈列表。此列表表示为 Error 对象上的一个属性,名为 stack。堆栈包括错误消息、函数名称以及函数的源文件位置信息。这些信息可以通过了解正在调用哪个函数,甚至看到哪行代码存在问题来帮助开发人员快速诊断缺陷。例如,它可能表明传递给函数的参数是 null 或无效类型。

让我们通过一个简单的脚本示例来探索,该脚本尝试计算两点 (0, 2) 和 (12, 10) 之间的距离。

(function () {
    'use strict';
    function squareRoot(n) {
        if (n < 0)
            throw new Error('Cannot take square root of negative number.');
        return Math.sqrt(n);
    }
    
    function square(n) {
        return n * n;
    }
    
    function pointDistance(pt1, pt2) {
        return squareRoot((pt1.x - pt2.x) + (pt1.y - pt2.y));
    }
    
    function sample() {
        var pt1 = { x: 0, y: 2 };
        var pt2 = { x: 12, y: 10 };
        
        console.log('Distance is: ' + pointDistance(pt1, pt2));
    }
    try {
        sample();
    }
    catch (e) {
        console.log(e.stack);
    }
})();

 

此脚本存在一个 bug – 它忘记对分量差进行平方。因此,对于某些输入,pointDistance 函数将简单地返回不正确的结果;其他时候,它会导致错误。为了理解堆栈跟踪,让我们在 F12 开发人员工具中检查错误,并查看其脚本选项卡。

堆栈跟踪被转储到 catch 子句中的控制台,并且由于它位于堆栈的顶部,因此很容易看出错误源自 squareRoot 函数。为了调试问题,开发人员无需深入到堆栈跟踪中;squareRoot 的前置条件被违反了,向上查看堆栈一层,原因很清楚:对 squareRoot 的调用中的子表达式本身应该是平方的参数。

在调试时,stack 属性可以帮助标识设置断点的代码。请记住,还有其他查看调用堆栈的方法:例如,如果您将脚本调试器设置为“Break on caught exception”(在捕获的异常时中断)模式,您可能可以在调试器中检查调用堆栈。对于已部署的应用程序,您可以考虑将有问题的代码包装在 try/catch 中,以捕获失败的调用,并将它们记录到您的服务器。然后,开发人员将能够查看调用堆栈以帮助隔离问题区域。

DOM 异常和 Error.stack

我之前提到,抛出的对象必须是 Error 或通过其原型链回溯到 Error。这是故意的;JavaScript 支持将任何对象甚至原始值作为异常抛出。虽然所有这些都可以被捕获和检查,但并非所有都专门设计用于包含错误或诊断信息。因此,只有 Error 在抛出时才会被更新 stack 属性。

即使 DOM 异常是对象,它们也没有回溯到 Error 的原型链,因此它们没有 stack 属性。在执行 DOM 操作并希望显示与 JavaScript 兼容的错误的情况下,您可能希望将 DOM 操作代码包装在 try/catch 块中,并在 catch 子句中抛出一个新的 Error 对象。

function causesDomError() {
    try {
        var div = document.createElement('div');
        div.appendChild(div);

} catch (e) {

throw new Error(e.toString());

}

}

但是,您可能需要考虑是否要使用此模式。它可能最适合实用工具库的开发;特别是,请考虑您的代码的意图是隐藏 DOM 操作,还是仅仅执行一项任务。如果它隐藏 DOM 操作,则包装操作并抛出 Error 可能是正确的方法。

性能考量

堆栈跟踪的构建始于错误对象被抛出时;这样做需要遍历当前执行堆栈。为了防止在遍历非常大的堆栈(可能甚至是递归堆栈链)时出现性能问题,默认情况下,IE 只收集顶部的 10 个堆栈帧。不过,可以通过将静态属性 Error.stackTraceLimit 设置为另一个值来配置此设置。此设置是全局的,并且必须在抛出错误之前更改,否则它不会影响堆栈跟踪。

异步异常

当从异步回调(例如,timeout、interval 或 XMLHttpRequest)生成堆栈跟踪时,异步回调而不是创建异步回调的代码将位于调用堆栈的底部。这对于跟踪出错的代码有一些潜在的影响:如果您为多个异步回调使用相同的回调函数,您可能会发现仅通过检查很难确定哪个回调导致了错误。让我们稍微修改一下之前的示例,使其不直接调用 sample(),而是将其放入一个 timeout 回调中。

(function () {
    'use strict';
    function squareRoot(n) {
        if (n < 0)
            throw new Error('Cannot take square root of negative number.');
        
        return Math.sqrt(n);
    }
    
    function square(n) {
        return n * n;
    }
    
    function pointDistance(pt1, pt2) {
        return squareRoot((pt1.x - pt2.x) + (pt1.y - pt2.y));
    }
    
    function sample() {
       var pt1 = { x: 0, y: 2 };
       var pt2 = { x: 12, y: 10 };
       
       console.log('Distance is: ' + pointDistance(pt1, pt2));
    }
    
    setTimeout(function () {
        try {
            sample();
        }
        catch (e) {
            console.log(e.stack);
        }
    }, 2500);
})();

执行此代码段后,您将在稍有延迟后看到堆栈跟踪。这次,您还会看到堆栈的底部不是全局代码,而是 Anonymous 函数。实际上,它不是同一个匿名函数,而是传递给 setTimeout 的回调函数。由于您丢失了连接回调的上下文,因此可能无法确定是什么导致回调被调用。如果您考虑一个场景,其中一个回调被注册来处理多个不同按钮的点击事件,您将无法确定注册指的是哪个回调。也就是说,这个限制只是轻微的,因为在大多数情况下,堆栈的顶部很可能会突出问题区域。

探索 Test Drive 演示

 

请查看此 Test Drive 演示,使用 Windows 8 Release Preview 中的 IE10。您可以在 eval 的上下文中执行代码,如果发生错误,您将能够检查它。如果您在 IE10 中运行代码,当您将鼠标悬停在堆栈跟踪的错误行上时,您还可以突出显示您的代码行。您可以自己输入代码到 Code 区域,或者从列表中选择一些示例。您还可以设置运行代码示例时的 Error.stackTraceLimit 值。

作为参考资料,您可能想查阅 MSDN 上关于 Error.stackstackTraceLimit 的文档。

本文由 Rob Paveza 撰写。Rob 是 Internet Explorer 团队的项目经理,专门研究新的 JavaScript 运行时引擎 Chakra 

HTML5 视频资源

© . All rights reserved.