Safari (iOS) 上调试 JavaScript 的 8 个简单步骤

调试 JavaScript 是 Web 开发中不可避免的一部分,但不是最愉快的工作。当你已经忙得不可开交时,调试任务总会突然出现,而且常常是由于某个在测试中被忽略的问题,已经自上次发布以来一直在引发困扰。

这就是为什么配备能够帮助你更快调试的开发者工具非常有帮助的原因。

如果你已经制作了一个移动友好的网站,但某些功能出现了问题——比如说,联系表单的提交按钮在移动设备上不起作用——你无法只按下 Option + ⌘ + C 并在浏览器控制台中查找错误。你需要学会如何在桌面上调试移动设备上的问题!

本文将重点讲解如何使用桌面版 Safari Web 检查器调试在 iOS上设备上运行的 JavaScript 代码。

第 1 步:示例项目介绍

为了展示如何使用 Safari 的 Web 检查器调试应用程序,我们将使用一个简单的 “添加人员” 表单。该表单允许你输入名字、中间名和姓氏。点击 “保存” 按钮后,表单会进行一些处理,然后将数据发送到你(假想的)服务器。

该表单的代码包含三个功能:

  • 一个点击处理程序
  • 一个字符串大写函数
  • 一个保存功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var saveButton = document.getElementById('saveButton');
var firstNameField = document.getElementById('firstName');
var middleNameField = document.getElementById('middleName');
var lastNameField = document.getElementById('lastName');

function onSaveButtonClick(){
var firstName = firstNameField.value;
var middleName = middleNameField.value;
var lastName = lastNameField.value;

// 大写姓名
firstName = capitalizeString(firstName);
middleName = capitalizeString(middleName);
lastName = capitalizeString(lastName);

doSave(firstName, middleName, lastName);
}

function capitalizeString(value){
return value.split('')[0].toUpperCase() + value.slice(1);
}

function doSave(firstName, middleName, lastName){
alert(firstName + ' ' + middleName + ' ' + lastName + ' 已保存!');
}

saveButton.addEventListener('click', onSaveButtonClick);

你开始调试时出现了一个 bug,你需要尽快修复它。

第 2 步:错误信息


你需要用来调试错误的信息位于堆栈跟踪模块中。

堆栈跟踪的 “消息” 部分是关于错误的简要概述。在这种情况下,toUpperCase 方法被调用在一个未定义的值上。

堆栈跟踪告诉你错误发生的位置以及导致该错误的函数调用顺序。正如上面的截图所示,错误发生在 capitalizeString 函数中,位于 index.js 文件的第 20 行。

知道触发错误的行意味着你可以直接跳转到出错的位置,开始深入研究问题的根源。

第 3 步:设置并连接你的设备

在我们开始使用 Web 检查器调试应用程序之前,我们需要为开发启用 iOS 和 OSX 浏览器。

在 iOS 上启用 Web 检查器

在你的 iOS 设备上,进入 “设置” 应用,向下滚动并选择 “Safari”。

Web 检查器调试 Safari

在 Safari 设置页面中,向下滚动至底部,选择 “高级” 选项。在高级部分,打开 “Web 检查器” 选项。

在 macOS 上启用开发者菜单

设置好设备后,我们需要启动 macOS 上的 Safari 并启用开发者菜单。点击 “Safari” 菜单项,选择 “偏好设置”,打开 Safari 的偏好设置对话框。

在偏好设置对话框中,进入“高级”选项卡,然后勾选底部的 “在菜单栏中显示开发者菜单” 选项。

现在你可以关闭偏好设置对话框。

连接设备

为了让 macOS 上的 Safari 可以与 iOS 设备通信,我们需要通过标准 USB 数据线将设备连接到电脑。

打开示例应用程序

最后,让我们在 iOS 设备上使用 Safari 浏览器的新标签页打开示例 Web 应用程序。准备好之后,我们就可以开始调试了!

第 4 步:探索 Web 检查器的结构

现在 Safari 浏览器已经准备就绪,并且设备也已连接,我们可以打开 Web 检查器。

打开 “开发者” 菜单,你应该能在菜单顶部附近看到你的设备名称(例如 “Jeff 的 iPhone”)。将鼠标悬停在此项上,菜单会展开。新菜单将显示在移动版 Safari 中打开的所有标签页,以及可以连接 Web 检查器的标签页。点击与示例应用程序相关的项目。

Web 检查器现在将打开,我们准备开始调试了。你现在看到的 Web 检查器与在桌面版 Safari 中调试页面时看到的完全相同,只不过这是一个指向你 iOS 设备上某个打开标签页的独立实例。

在 Web 检查器窗口中,控制台 (Console) 标签会处于活动状态(如果没有,选择它)。此标签允许你随时执行任意 JavaScript 代码或查看来自 console.log 的输出。

尝试输入 alert('Hello!') 并按下 Enter 键,你应该会立刻看到弹出框。

控制台 (Console) 标签是一个非常有价值的调试工具,你可以将它作为草稿本,用来尝试代码并评估变量,以帮助诊断问题。

要调试代码,首先需要能够在 Web 检查器中导航浏览源代码。你可以在 “源代码” (Sources) 标签中完成此操作。

此标签左侧窗格的下半部分列出了页面中加载的所有源文件。你可以点击这些文件名,内容会显示在中央窗格中。你还可以通过点击标签右上角的按钮展开一个包含调试工具的额外窗格。

如果文件很多,你可以通过按下 macOS 上的 CMD-P 快捷键,然后开始输入文件名来进行搜索。

在应用程序中,你知道问题出现在 index.js 文件中,因此从左侧的列表中选择它来查看其内容。

第 5 步:为代码添加断点

现在你已经能够查看代码了,我们希望能够逐行调试代码,查看问题出在哪里。为此,我们使用断点。断点是在代码的特定位置添加的标记,当执行到该点时会暂停执行,方便你检查代码的状态,并逐行执行代码。

有几种不同的方式可以添加断点:

行断点

最常见的添加断点方式是在你希望暂停的位置找到具体的行,并在该行添加断点。导航到你感兴趣的文件和行,然后点击行号。在该行会出现一个蓝色标记,每次执行到此行时都会暂停代码执行。在下面的截图中,断点会在 index.js 的第 7 行暂停。

你还会注意到,左上角会保留一个你添加的断点列表。这个列表对于在大型文件中快速导航到断点非常有用,也可以通过点击左侧窗格中条目旁边的标记临时禁用断点。

要删除断点,右键点击行标记并选择 “删除断点”。

编程断点

你还可以通过编程方式添加断点,这在你不想在 Web 检查器中搜索代码,或者已经在集成开发环境 (IDE) 中打开代码时非常有用。你还可以使用这种方式有条件地引入断点,例如在某些循环的迭代中,或者在代码加载页面时没有时间手动添加断点的情况下。

要做到这一点,你可以在希望暂停执行的位置添加 debugger; 语句。下面的代码将与上面的行断点产生相同的效果。

错误断点

Web 检查器有一个非常实用的功能,它会在代码中遇到异常时停止执行,允许你检查错误发生时的情况。你甚至可以选择在异常已经被 try/catch 语句处理的情况下也停止。

要在任何异常发生时停止执行,可以点击 “All Exceptions” 标签旁边的箭头图标。激活时图标会变暗。如果你只想在未捕获的异常发生时停止执行,可以点击 “Uncaught Exceptions” 标签旁边的箭头图标。

第 6 步:逐步执行代码

现在我们已经知道如何在代码中设置断点了,接下来我们想逐行执行代码,以找出问题所在。首先,在第 7 行添加一个断点——就在 Add 按钮的点击处理函数内部,这样我们可以从头开始调试。

在上一节中,我们从 Raygun 的错误报告推测出错误来自 capitalizeString 方法。这个方法被调用了三次,那么到底是哪次调用引发了问题呢?你可以更仔细地查看 Stacktrace,发现错误是由第 13 行的调用引发的。你知道第 13 行与 Middle Name 值有关。因此,你应该专注于通过正确构造输入来重现错误。

有了这些信息,你可以填写 First Name 和 Last Name 字段,但将 Middle Name 留空,看看是否会触发错误。点击 Save 按钮后,Sources 标签将打开,你可以看到断点已经激活。现在你可以开始逐步执行代码。你可以使用 Sources 窗格左上角的按钮来完成这一操作。

    1. 第一个图标是断点箭头,用于启用或禁用所有断点
    1. 第二个按钮用于暂停/恢复代码的执行,继续运行直到下一个断点
    1. 第三个图标是一个箭头越过一行,表示“跳过”当前行,移动到下一行
    1. 指向行的箭头“进入”当前行中的下一个函数调用
    1. 最后一个图标是指向行外的箭头,表示“跳出”当前函数调用,返回调用栈的上一层

你将使用这些按钮逐步执行代码,直到到达 capitalizeString 函数。从第 7 行开始,使用“跳过”按钮直到我们到达第 13 行。活动行会以绿色背景显示。现在你可以使用“进入”按钮,跳转到 capitalizeString 函数的调用,代码执行将从第 13 行跳到第 20 行。

调用栈导航

当你逐步执行代码时,可能会想返回上一级函数以检查当时发生了什么。你可以使用 “调用栈” (Call Stack) 部分来完成这项操作,它列出了代码执行中通过的所有函数——与 Raygun 错误报告中的调用栈完全一致。

你可以点击列表中的任意一项,系统会将你跳转回该函数。需要注意的是,当前的执行位置不会改变,所以继续使用跳过按钮时,会从调用栈顶部开始。

第 7 步:确定应用程序状态

现在你已经导航到错误发生的地方,我们需要检查应用程序的状态,并找出导致错误的原因。

有多种方法可以查看变量的值和评估表达式,下面我们会逐一介绍。

鼠标悬停

确定变量值的最简单方法是将鼠标悬停在变量上,工具提示会弹出显示变量的值。你甚至可以选择一组表达式并将鼠标悬停在选择区域上,以获取表达式的输出。

监视表达式窗口

你可以将表达式添加到 Watch Expressions 面板,该面板会随着代码的执行显示表达式的当前值。Watch Expressions 面板非常方便,可以帮助你跟踪复杂表达式随时间的变化。

你可以通过点击面板顶部的 “+” 按钮,输入要观察的表达式并按下回车键来添加这些表达式。

局部变量

Watch Expressions 面板下方的 Local Variables (局部变量) 部分显示了当前范围内的变量列表及其对应的值。Local Variables 面板与 Watch Expressions 面板相似,但它是由 Web 检查器自动生成的。这个部分适合用来识别局部变量,而不需要将它们显式添加到 Watch Expressions 列表中。

控制台

最后,控制台 (Console) 标签是检查表达式值和试验代码的好工具。只需切换回控制台标签,输入一些代码并按下回车键,Web 检查器将会在当前断点的上下文和作用域内执行这些代码。

第 8 步:修复错误

切换到控制台标签,让我们开始分析导致错误的那行代码并进行修复。

首先,检查 value.split('') 的输出(它的意图是获取字符串的第一个字符,然后调用 toUpperCase 函数)。

在控制台中执行该表达式会发现它返回了一个空数组——这就是错误的根源!由于它返回了一个空数组,而我们试图对数组的第一个元素调用 toUpperCase(由于数组没有元素,返回的是 undefined),因此导致了错误。

你可以通过在控制台中输入完整表达式来验证这一点。要解决这个问题,你需要在解析字符串之前检查字符串是否为空或未定义。如果是,你需要返回一个空字符串,而不进行任何处理。

1
2
3
4
5
6
7
function capitalizeString(value){
if(!value || value.length === 0){
return '';
}

return value.split('')[0].toUpperCase() + value.slice(1);
}

总结

这就是使用 Web 检查器在移动版 Safari 中调试 JavaScript 的快速指南。启用远程调试非常简单,利用 Safari Web 检查器可以完全调试 iOS 设备上打开的页面。幸运的是,移动端调试工具已经取得了长足的进步,追踪移动端特定的 Bug 变得更加容易。

掌握这些工具并运用到下次调试 Safari 移动端 Bug 时,确实是非常值得的。花点时间熟悉这些工具,会让你的调试技能更上一层楼!此外,Web 检查器还有很多其他强大的功能,建议你花时间去探索一下。