CoffeeBean

JavaScript 调试和错误处理

Posted by admin in 中国足球队世界杯 on 2026-01-22 09:26:40

JavaScript 错误类型回顾

在本模块的早期,在出了什么问题?中,我们大致探讨了 JavaScript 程序中可能发生的错误类型,并指出它们大致可分为两种类型——语法错误和逻辑错误。我们还帮助您理解了一些常见的 JavaScript 错误消息,并向您展示了如何使用console.log()语句进行简单的调试。

在本文中,我们将更深入地探讨可用于跟踪错误的工具,并研究如何从一开始就防止错误。

代码 Linting

在尝试跟踪特定错误之前,您应该首先确保您的代码有效。使用 W3C 的标记验证服务、CSS 验证服务和 JavaScript Linter(例如ESLint)来确保您的代码有效。这可能会消除一堆错误,让您可以专注于剩余的错误。

代码编辑器插件

一遍又一遍地将代码复制粘贴到网页上以检查其有效性并不方便。我们建议在您的代码编辑器上安装一个 linter 插件,这样您就可以在编写代码时收到错误报告。尝试在您的代码编辑器的插件或扩展列表中搜索 ESLint 并安装它。

常见的 JavaScript 问题

您需要注意一些常见的 JavaScript 问题,例如:

基本语法和逻辑问题(再次查看JavaScript 故障排除)。

确保变量等在正确的范围中定义,并且您不会遇到在不同位置声明的项之间的冲突(请参阅函数范围和冲突)。

关于this的混淆,包括它适用于哪个作用域,以及因此它的值是否是您预期的。您可以阅读什么是“this”?以获得一个简单的介绍;您还应该研究像这个这样的示例,它展示了一种典型的模式:将一个this作用域保存到单独的变量中,然后在嵌套函数中使用该变量,这样您就可以确保将功能应用于正确的this作用域。

在使用全局变量迭代的循环中错误地使用函数(更普遍地说是“作用域错误”)。

例如,在bad-for-loop.html(参阅源代码)中,我们使用一个用var定义的变量循环 10 次,每次创建一个段落并为其添加一个onclick事件处理程序。当点击时,我们希望每个段落显示一个包含其编号(创建时i的值)的警告消息。相反,它们都报告i为 11——因为for循环在调用嵌套函数之前完成了所有迭代。

最简单的解决方案是用 let 而不是 var 声明迭代变量——这样与函数关联的 i 值对于每次迭代都是唯一的。请参阅 good-for-loop.html(另请参阅 源代码)以获取一个可用的版本。

确保异步操作已完成,然后再尝试使用它们返回的值。这通常意味着理解如何使用Promise:适当使用await,或在 Promise 的then()处理程序中运行代码以处理异步调用的结果。有关此主题的介绍,请参阅如何使用 Promise。

注意:有 Bug 的 JavaScript 代码:JavaScript 开发者最常犯的 10 个错误对这些常见错误及更多内容有一些不错的讨论。

浏览器 JavaScript 控制台

浏览器开发者工具具有许多有用的功能,可帮助调试 JavaScript。首先,JavaScript 控制台会报告代码中的错误。

在本地复制我们的fetch-broken示例(另请参阅源代码)。

如果您查看控制台,会看到一条错误消息。确切的措辞因浏览器而异,但会类似于:“Uncaught TypeError: heroes is not iterable”,并且引用的行号是 25。如果我们查看源代码,相关代码段是这样的:

jsfunction showHeroes(jsonObj) {

const heroes = jsonObj["members"];

for (const hero of heroes) {

// …

}

}

因此,一旦我们尝试使用 jsonObj(您可能期望它是一个 JSON 对象),代码就会崩溃。这应该使用以下 fetch() 调用从外部 .json 文件中获取:

jsconst requestURL =

"https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json";

const response = fetch(requestURL);

populateHeader(response);

showHeroes(response);

但这失败了。

控制台 API

您可能已经知道这段代码有什么问题,但让我们进一步探讨一下如何进行调查。我们将从 Console API 开始,它允许 JavaScript 代码与浏览器的 JavaScript 控制台进行交互。它有许多可用功能;您已经遇到过 console.log(),它会在控制台中打印自定义消息。

尝试添加一个 console.log() 调用来记录 fetch() 的返回值,像这样:

jsconst requestURL =

"https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json";

const response = fetch(requestURL);

console.log(`Response value: ${response}`);

populateHeader(response);

showHeroes(response);

在浏览器中刷新页面。这次,在错误消息之前,您会在控制台中看到一条新消息:

Response value: [object Promise]

console.log() 的输出显示,fetch() 的返回值不是 JSON 数据,而是一个 Promise。fetch() 函数是异步的:它返回一个 Promise,只有在实际从网络接收到响应后才会 fulfilled。在使用响应之前,我们必须等待 Promise 被 fulfilled。

console.error() 和调用堆栈

简要离题一下,让我们尝试使用不同的控制台方法来报告错误——console.error()。在您的代码中,替换:

jsconsole.log(`Response value: ${response}`);

with

jsconsole.error(`Response value: ${response}`);

保存代码并刷新浏览器,您现在会看到该消息报告为错误,具有与下方未捕获错误相同的颜色和图标。此外,消息旁边现在会有一个展开/折叠箭头。如果您按下它,您会看到一行告诉您错误源自 JavaScript 文件中的哪一行。事实上,未捕获的错误行也有这个,但它有两行:

showHeroes https://:7800/js-debug-test/index.js:25

https://:7800/js-debug-test/index.js:10

这意味着错误来自 showHeroes() 函数的第 25 行,正如我们之前所指出的。如果您查看代码,您会看到第 10 行的匿名调用是调用 showHeroes() 的行。这些行被称为调用堆栈,在尝试跟踪涉及代码中许多不同位置的错误源时非常有用。

在这种情况下,console.error() 调用并不是那么有用,但如果尚无可用调用堆栈,它可用于生成调用堆栈。

修复错误

无论如何,让我们回到尝试修复我们的错误。我们可以通过将 then() 方法链式调用到 fetch() 调用的末尾来访问已兑现 Promise 的响应。然后,我们可以将生成的响应值传递给接受它的函数,如下所示:

jsfetch(requestURL).then((response) => {

populateHeader(response);

showHeroes(response);

});

保存并刷新,看看您的代码是否正常工作。剧透一下——上述更改并未解决问题。不幸的是,我们仍然有相同的错误!

注意:总结一下,任何时候出现问题,并且某个值在代码的某个点看起来不是它应该有的值,您都可以使用 console.log()、console.error() 或其他类似的函数来打印出该值并查看发生了什么。

使用 JavaScript 调试器

让我们使用浏览器开发者工具中更复杂的功能进一步调查这个问题:在 Firefox 中称为JavaScript 调试器。

注意:其他浏览器也提供类似工具;Chrome 中的“Sources”标签页、Safari 中的调试器(参阅Safari Web 开发工具)等。

在 Firefox 中,调试器选项卡看起来像这样:

在左侧,您可以选择要调试的脚本(在此示例中我们只有一个)。

中心面板显示所选脚本中的代码。

右侧面板显示与当前环境相关的有用详细信息——断点、调用堆栈和当前活动的作用域。

此类工具的主要功能是能够向代码添加断点——这些是代码执行停止的点,此时您可以检查当前环境的状态并查看正在发生的事情。

我们来探索一下断点的使用

错误仍然在与之前相同的行抛出——for (const hero of heroes) {——在下面的截图中是第 26 行。单击中心面板中的行号以添加断点(您会看到一个蓝色箭头出现在其上方)。

现在刷新页面(Cmd/Ctrl + R)——浏览器将暂停在该行执行代码。此时,右侧将更新显示以下内容:

在断点下,您将看到已设置断点的详细信息。

在调用堆栈下,您会看到几个条目——这基本上与我们在 console.error() 部分中看到的调用堆栈相同。调用堆栈显示了导致当前函数被调用的函数列表。最上面是 showHeroes(),我们当前所在的函数,其次是 onload,它存储了包含对 showHeroes() 调用的事件处理函数。

在作用域下,您将看到我们正在查看的函数的当前活动作用域。我们只有三个——showHeroes、block和Window(全局作用域)。每个作用域都可以展开以显示代码执行停止时作用域内变量的值。

我们可以在这里找到一些非常有用的信息

展开 showHeroes 作用域——您可以从中看到 heroes 变量是 undefined,这表明访问 jsonObj 的 members 属性(函数的第 一行)没有成功。

您还可以看到 jsonObj 变量存储的是一个 Response 对象,而不是一个 JSON 对象。

showHeroes() 的参数是 fetch() promise 成功完成后的值。所以这个 promise 不是 JSON 格式的:它是一个 Response 对象。还需要额外一步才能将响应内容作为 JSON 对象检索。

我们希望您自己尝试解决这个问题。为了帮助您入门,请参阅 Response 对象的文档。如果您遇到困难,可以在 https://github.com/mdn/learning-area/tree/main/tools-testing/cross-browser-testing/javascript/fetch-fixed 找到修复后的源代码。

注意:调试器选项卡还有许多其他有用的功能,我们在这里没有讨论,例如条件断点和监视表达式。有关更多信息,请参阅调试器页面。

在代码中处理 JavaScript 错误

HTML 和 CSS 是宽容的——由于语言的性质,错误和未识别的功能通常可以被处理。例如,CSS 会忽略未识别的属性,而其余代码通常仍能正常工作。然而,JavaScript 不像 HTML 和 CSS 那样宽容——如果 JavaScript 引擎遇到错误或无法识别的语法,它通常会抛出错误。

让我们探讨一种在代码中处理 JavaScript 错误的常见策略。以下部分旨在通过将以下模板文件复制为本地计算机上的 handling-errors.html,在开头和结尾的 标签之间添加代码片段,然后在浏览器中打开文件并查看开发工具 JavaScript 控制台中的输出来进行操作。

html

Handling JS errors

条件请求

JavaScript 条件语句的一个常见用途是处理错误。条件语句允许您根据变量的值运行不同的代码。通常,您会希望防御性地使用它,以避免在值不存在或类型错误时抛出错误,或在值导致返回不正确结果(这可能会在以后导致问题)时捕获错误。

我们来看一个例子。假设我们有一个函数,它接受一个等于用户身高(英寸)的参数,并以米为单位返回他们的身高,精确到小数点后两位。这可能看起来像这样:

jsfunction inchesToMeters(num) {

const mVal = (num * 2.54) / 100;

const m2dp = mVal.toFixed(2);

return m2dp;

}

在您的示例文件的