本系列其他译文请看[JS工作机制 - 小白1991的专栏 - 掘金 (juejin.cn)] (juejin.cn/column/6988…
本文阅读指数:2 都是基本的调试方式,价值不太高
调试是软件开发的一个重要部分,任意开发者在这个领域都可以深入的提升。在发布环境中遗留下重大bug会给你的应用和公司带来巨大的损失。调试程序包括找bug和修Bug 在调试过程中,你可以单步调试代码,确定异常发生的额地方,以及引起异常的原因。所有的现代浏览器都内置了JS提示器。JS内部也有一些方法和标记,方便我们调试。 在JS中调试同步和异步代码略有不同。因为,在异步中,函数的调用和返回是不按顺序的。如果在规定时间内没有响应,超时也是一个bug.这一章,我们会讨论如何在JS中调试,以及调试异步代码的提示。
为什么调试很重要
调试通过查找bug和不正确代码,来避免应用中大多数的不正确行为。 bug经常让你的应用崩溃。使用适当的调试,你可以从代码中发现问题,避免一些问题。 例如,使用调试你可以确定运行时或编译时错误的原因。一些代码或许不会抛出异常或错误,但是会变现异常。例如,指定一些不正确的变量引发一些意外的行为。
let a = 5;
let b = 10;
let c = 12;
let d = a + b;// Developer was meaning to write let e = a + c;
let e = b + c;
上面代码中,开发之期待17
,但是获得了一个22
。如果开发者使用console.log()
调试代码,他们可以在控制台跟踪异常。有时候,开发者使用一些工具和程序开创建应用。用正确的调试,开发者可以确定他们的应用整体的表现。
如何调试JS应用
大多是web应用都使用JS,手机应用也逐渐使用JS了。在推送他们发布环境之前,开发者需要调试它们。
有时,开发者需要比写代码更加注意。看一下调试JS应用的调试过程:
定位Bug
调试的第一步是定位bug。工程师可以异常发生时或者只是检查代码来发现bug。
调试要么是主动地,要么是被动的。被动的调试,由开发者来掌控调试步骤。换句话说,就是开发者可以从执行步骤中收集日志和有效数据。
但是,有一些场景需要开发者在调试时跟应用通信。例如,开发者可能需要给应用发送数据,以便调试应用的行为表现。这种场景使用被动调试时无法满足的,因为被动调试只包含执行调试步骤和打印调试程序中的数据。因此,开发者使用主动方式来调试。主动调试可以让开发者跟应用进行通信
分离bug
分离bug的过程是确定程序的哪一块代码引起了异常。在这一步,开发者尝试从应用中分离引起异常的代码部分,这样可以更加彻底的调试。这个步骤一般包含这些方法:
1.正向分析
这个方法,开发者向前单独调试代码,有时放置一些断点。一旦有异常被记录到,开发者就聚焦于这块区域,确定异常的原因。
2. 反向分析
有时候,开发者需要反向跟踪异常,从当前的断点到前面的断点去查找异常的原因。这就是反向分析
确定bug原因
分离bug之后,就是调试分离的部分来确定原因。例如,如果异常围绕一个不正确的输入字段,就会调试所有的输入字段以确定bug原因。
修复Bug
一旦确定了Bug原因,下一步就是修复。如果发现这是个比较深层的问题,关系到应用架构,就需要更加资深的开发者来处理它
测试应用
调试是一个反复的过程,从开发一直到上线。修复Bug之后,开发者要继续测试,看是否有其他问题。有时候,修复bug之后反而生出更多的Bug。
JS中的测试方法
这一节,我们要讨论调试JS的一些方式:
Web 控制台
多数浏览器都带有控制台,方便开发者记录页面有关的信息。控制台的一个优势是,你可以在上面执行JS表达式。 来看一些跟控制台有关的调试方法吧。 假如我们像调试的是这段代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Debugging Test</title>
</head>
<body>
<p id="demo">Click the button to display greeting.</p>
<button onclick="myFunction()">Display Greeting</button>
<script>
function myFunction() {
document.getElementById("demo").innerHTML = "Hello World";
}
</script>
</body>
</html>
- 日志
快速调试,使用console.log()
方法。
在我们的例子中,我们想要确保按钮工作正常,就要确保调用的 id
是正确的。当我们调用的 id
和按钮的 id
不一致时,我们可以log一个信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Debugging Test</title>
</head>
<body>
<p id="demo">Click the button to display greeting.</p>
<button onclick="myFunction()">Display Greeting</button>
<script>
function myFunction() {
let id = document.getElementById("demo").id;
let correctId = "demo";
if (id === correctId) {
document.getElementById("demo").innerHTML = "Hello World";
}else console.log ("ID doesn't match, id is", id.id)
}
</script>
</body>
</html>
这里我们不会得到任何日志,因为我们调用的“demo”id和按钮的id是一致的。如果开发者指定按钮id
为“demi”,那就可以指定demi
到correctId
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Debugging Test</title>
</head>
<body>
<p id="demo">Click the button to display greeting.</p>
<button onclick="myFunction()">Display Greeting</button>
<script>
function myFunction() {
let id = document.getElementById("demo").id;
let correctId = "demi";
if (id === correctId) {
document.getElementById("demo").innerHTML = "Hello World";
} else console.log("ID doesn't match, id is", id);
}
</script>
</body>
</html>
现在,我们就获得了一个log,
ID doesn’t match, id is demo
。使用这个信息,开发者知道他们给按钮指派错了id
2. 警告
有时候控制台里太多的log,很难快速定位到问题。为了让重要信息脱颖而出,可以使用 console.warn()
方法。这个方式跟console.log()
很类似,但是它以警告的方式输出日志。
同样的例子,我们使用console.warn()
替代console.log()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Debugging Test</title>
</head>
<body>
<p id="demo">Click the button to display greeting.</p>
<button onclick="myFunction()">Display Greeting</button>
<script>
function myFunction() {
let id = document.getElementById("demo").id;
let correctId = "demi";
if (id === correctId) {
document.getElementById("demo").innerHTML = "Hello World";
} else console.warn("ID doesn't match, id is", id);
}
</script>
</body>
</html>
3. 错误
有时候,异常日志能更容易被关注到。我们可以使用console.error()
方法做到。同样,使用console.error()
来改变上面的例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Debugging Test</title>
</head>
<body>
<p id="demo">Click the button to display greeting.</p>
<button onclick="myFunction()">Display Greeting</button>
<script>
function myFunction() {
let id = document.getElementById("demo").id;
let correctId = "demi";
if (id === correctId) {
document.getElementById("demo").innerHTML = "Hello World";
} else console.error("ID doesn't match, id is", id);
}
</script>
</body>
</html>
4. 断言
如果断言失败,
console.assert()
将会在控制台写一个异常。同样,改造一下我们的例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Debugging Test</title>
</head>
<body>
<p id="demo">Click the button to display greeting.</p>
<button onclick="myFunction()">Display Greeting</button>
<script>
function myFunction() {
let id = document.getElementById("demo").id;
let correctId = "demi";
const errorMsg = "the ID doesn't match the correct ID";
console.assert(id === correctId, { errorMsg: errorMsg });
if (id === correctId) {
document.getElementById("demo").innerHTML = "Hello World";
}
}
</script>
</body>
</html>
5. Debugger 声明
debugger用来触发一个debbugger或者暂停代码的执行。它用来给你的应用设置断点。调试的时候使用断点是非常棒的,因为它允许开发者在应用执行时探查程序。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Debugging Test</title>
</head>
<body>
<p id="demo">Click the button to display greeting.</p>
<button onclick="myFunction()">Display Greeting</button>
<script>
function myFunction() {
let id = document.getElementById("demo").id;
let correctId = "demi";
debugger;
document.getElementById("demo").innerHTML = "Hello World";
}
</script>
</body>
</html>
使用调试器来调试JS
调试工具也是调试器,他们提供了增强的可视化和分析,帮助开发者快速定位和修复bug。我们看看Firefox的调试工具把:
Firefox JS Debugger
Firefox的JS调试器,允许你在本地和远程调试你的JS应用。你可以设置断点,事件,调试线程等等。
开始调试
首先打开你的调试器,可以使用快捷键 CTRL
+ SHIFT
+ I
,然后切换到debugger
面板。这样,调试器就打开了
注意,调试面板被分为3个部分,第一部分包含了页面必须的文件,第二部分包含了开发者感兴趣的文件(也就是你要调试的文件),第三部分包含调试需要的工具。
为了打开我们的文件,在第一个面板点击
file://
文件夹,然后选择选择你关系的文件、
暂停执行
调试时,断点会暂停代码执行,这一节中,我们会看看不同类型的断点
无条件断点
这一节我们使用console.error()
的例子。当你点击文件的行数位置,就会添加一个断点,这就是一个无条件的断点。你也可以使用右键菜单来Add breakpoint
我们在第16行添加一个无条件断点,如果 (id === correctId)
,你就会得到一个“Paused at execution”的提示
现在,你就可以单步调试你的代码了。在调试过程中,可能有一些意外的日志。例如,在第19行,你将会得到异常信息。注意,不能在HTML代码中添加断点。
条件断点
当你定义的表达式值为true
时,你的断点才会生效,这叫条件断点。例如,在16行:if (id === correctId) {
添加一个下面的条件,就不会引起中断,因为id
与correctId
不一致
id != correctId
如果我们改变了
id === correctId
的条件,就不会触发中断。
事件监听断点
开发者还可以给断点或者异常暂停添加事件监听器。在第三个面板中打开Event Listener Breakpoints 选项,然后点击你选择的任意的事件监听器。
如果你想无论任何时候程序发生异常,都可以执行暂停,选择“Pause on Exceptions”。
单步调试
代码暂停时,就可以单步调试,观察异常。你可以step over, step in, 或者 step out。 step over是执行函数内下一行代码,但不会进入子函数。 Step in 遇到子函数就进入。 Step OUT会执行完当前的函数
调试异步代码的提示
异步调试跟同步调试有点不一样。 异步代码在主线程执行之后才运行,不会阻塞JS代码的运行。在这个过程中,如果超时或者无响应,就会有异常。
调试异步JS代码,需要记住这些提示:
- 发生超时异常,可能是代码也可能是网络连接引起。要从提升超时事件和检查网络连接入手。
- 异步代码的数据不是按顺序的,调试时选择“Pause on Exception” 按钮。当你获取异常时就会暂停执行。使用step over 来探查整个调用栈。
- 异步调试的第一步是在不同的代码片段中使用
console.log()
方法 - 使用
try
和catch
声明来处理异常