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





4.00/5 (1投票)
这些应用程序日益强大和复杂,意味着开发人员需要更好的工具,例如 Error.stack,来处理错误和诊断 bug。在本文中,我将向您展示一些简单的调试技巧,以帮助您节省时间。
现代浏览器(如 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.stack 和 stackTraceLimit 的文档。
本文由 Rob Paveza 撰写。Rob 是 Internet Explorer 团队的项目经理,专门研究新的 JavaScript 运行时引擎 Chakra
HTML5 视频资源
- 在 Office 和 SharePoint 中使用 HTML5 构建应用
- 在 Blend for Windows 8 中构建出色的 HTML 应用
- 为现代引擎构建高性能的 JavaScript
- 深入 WinJS:构建 Windows 应用商店应用
- 诊断基于 JavaScript 的 Windows 应用商店应用中的性能和内存问题
- 不要破坏网络:为什么 Web 标准很重要以及如何负责任地使用它们
- 从零到英雄!用 HTML5 构建一个 Windows 应用商店游戏
- 介绍 TypeScript:一种用于应用级 JavaScript 开发的语言
- 使用 HTML 和 JavaScript 创建 Windows 应用商店应用入门
- 点亮 Windows:从网站到应用
- 下一代现代 JavaScript
- 使用 jQuery 编写 Windows 应用商店应用