2025-04-11 日报 Day153

2025-04-11 日报 Day153

Yuyang 前端小白🥬

今日的鸡汤

珍惜、善用时间资源,才能浇灌梦想花开,眺望硕果累累。

今日学习内容

1、JS 红皮书 P675-693 第二十一章:错误处理与调试

今日笔记

1、 浏览器错误报告:

  • 桌面控制台: 所有现代桌面浏览器都会通过控制台暴露错误。这些错误可以显示在开发者工具内嵌的控制台中。在前面提到的所有浏览器中,访问开发者工具的路径是相似的。可能最简单的查看错误的方式就是在页面上单击鼠标右键,然后在上下文菜单中选择 Inspect(检查)或 Inspect Element(检查元素),然后再单击 Console(控制台)选项卡。
  • 移动控制台: 移动浏览器不会直接在设备上提供控制台界面。不过,还是有一些途径可以在移动设备中检查错误。Chrome 移动版和 Safari 的 iOS 版内置了实用工具,支持将设备连接到宿主操作系统中相同的浏览器。然后,就可以在对应的桌面浏览器中查看错误了。这涉及设备之间的硬件连接,且要遵循不同的操作步骤。
    2、错误处理: 错误处理在编程中的重要性毋庸置疑。所有主流 Web 应用程序都需要定义完善的错误处理协议,大多数优秀的应用程序有自己的错误处理策略,尽管主要逻辑是放在服务器端的。事实上,服务器端团队通常会花很多精力根据错误类型、频率和其他重要指标来定义规范的错误日志机制。最终实现通过简单的数据库查询或报告生成脚本就可以了解应用程序的运行状态。
  • try/catch语句: ECMA-262 第 3 版新增了 try/catch 语句,作为在 JavaScript 中处理异常的一种方式。基本的语法如下所示,跟 Java 中的 try/catch 语句一样:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    try { 
    // 可能出错的代码
    } catch (error) {
    // 出错时要做什么
    }
    // 任何可能出错的代码都应该放到 try 块中,而处理错误的代码则放在 catch 块中,如下所示:
    try {
    window.someNonexistentFunction();
    } catch (error){
    console.log("An error happened!");
    }
    如果 try 块中有代码发生错误,代码会立即退出执行,并跳到 catch 块中。catch 块此时接收到一个对象,该对象包含发生错误的相关信息。
    finnally子句: try/catch 语句中可选的 finally 子句始终运行。如果 try 块中的代码运行完,则接着执行finally 块中的代码。如果出错并执行 catch 块中的代码,则 finally 块中的代码仍执行。try 或catch 块无法阻止 finally 块执行,包括 return 语句。比如:
    function testFinally(){
    try {
    return 2;
    } catch (error){
    return 1;
    } finally {
    return 0;
    }
    }
    这个函数在 try/catch 语句的各个部分都只放了一个 return 语句。看起来该函数应该返回 2,因为它在 try 块中,不会导致错误。但是,finally 块的存在导致 try 块中的 return 语句被忽略。因此,无论什么情况下调用该函数都会返回 0。如果去掉 finally 子句,该函数会返回 2。如果写出finally 子句,catch 块就成了可选的(它们两者中只有一个是必需的)。
    错误类型: 代码执行过程中会发生各种类型的错误。每种类型都会对应一个错误发生时抛出的错误对象。ECMA-262 定义了以下 8 种错误类型:
     Error
     InternalError
     EvalError
     RangeError
     ReferenceError
     SyntaxError
     TypeError
     URIError
    Error 是基类型,其他错误类型继承该类型。
    InternalError 类型的错误会在底层 JavaScript 引擎抛出异常时由浏览器抛出。例如,递归过多导致了栈溢出。这个类型并不是代码中通常要处理的错误,如果真发生了这种错误,很可能代码哪里弄错了或者有危险了。
    EvalError 类型的错误会在使用 eval()函数发生异常时抛出。
    RangeError 错误会在数值越界时抛出。例如,定义数组时如果设置了并不支持的长度,如-20 或Number.MAX_VALUE,就会报告该错误。
    ReferenceError 会在找不到对象时发生。
    SyntaxError 经常在给 eval()传入的字符串包含 JavaScript 语法错误时发生。
    TypeError 在 JavaScript 中很常见,主要发生在变量不是预期类型,或者访问不存在的方法时。很多原因可能导致这种错误,尤其是在使用类型特定的操作而变量类型不对时。
  • 抛出错误: 与 try/catch 语句对应的一个机制是 throw 操作符,用于在任何时候抛出自定义错误。throw 操作符必须有一个值,但值的类型不限。下面这些代码都是有效的:
    throw 12345;
    throw “Hello world!”;
    throw true;
    throw { name: “JavaScript” };
    使用 throw 操作符时,代码立即停止执行,除非 try/catch 语句捕获了抛出的值。
    可以通过内置的错误类型来模拟浏览器错误。每种错误类型的构造函数都只接收一个参数,就是错误消息。下面看一个例子:
    throw new Error(“Something bad happened.”);
    以上代码使用一个自定义的错误消息生成了一个通用错误。浏览器会像处理自己生成的错误一样来处理这个自定义错误。换句话说,浏览器会像通常一样报告这个错误,最终显示这个自定义错误。当然,使用特定的错误类型也是一样的,如以下代码所示:
    throw new SyntaxError(“I don’t like your syntax.”);
    throw new InternalError(“I can’t do that, Dave.”);
    throw new TypeError(“What type of variable do you take me for?”);
    throw new RangeError(“Sorry, you just don’t have the range.”);
    throw new EvalError(“That doesn’t evaluate.”);
    throw new URIError(“Uri, is that you?”);
    throw new ReferenceError(“You didn’t cite your references properly.”);
    自定义错误常用的错误类型是 Error、RangeError、ReferenceError 和 TypeError。
    此外,通过继承 Error(第 6 章介绍过继承)也可以创建自定义的错误类型。创建自定义错误类型时,需要提供 name 属性和 message 属性,比如:
    class CustomError extends Error {
    constructor(message) {
    super(message);
    this.name = “CustomError”;
    this.message = message;
    }
    }
    throw new CustomError(“My message”);
  • error事件: 任何没有被 try/catch 语句处理的错误都会在 window 对象上触发 error 事件。
    在 onerror 事件处理程序中,任何浏览器都不会传入 event 对象。相反,会传入 3 个参数:错误消息、发生错误的 URL 和行号。
    window.onerror = (message, url, line) => {
    console.log(message);
    };
    在任何错误发生时,无论是否是浏览器生成的,都会触发 error 事件并执行这个事件处理程序。然后,浏览器的默认行为就会生效,像往常一样显示这条错误消息。可以返回 false 来阻止浏览器默认报告错误的行为,如下所示:
    window.onerror = (message, url, line) => {
    console.log(message);
    return false;
    };
  • 错误处理策略: 过去,Web 应用程序的错误处理策略基本上是在服务器上落地。错误处理策略涉及很多错误和错误处理考量,包括日志记录和监控系统。这些主要是为了分析模式,以期找到问题的根源并了解有多少用户会受错误影响。
    在 Web 应用程序的 JavaScipt 层面落地错误处理策略同样重要。因为任何 JavaScript 错误都可能导致网页无法使用,所以理解这些错误会在什么情况下发生以及为什么会发生非常重要。绝大多数 Web 应用程序的用户不懂技术,在碰到页面出问题时通常会迷惑。为解决问题,他们可能会尝试刷新页面,也可能会直接放弃。作为开发者,应该非常清楚自己的代码在什么情况下会失败,以及失败会导致什么结果。另外,还要有一个系统跟踪这些问题。
  • 识别错误: 错误处理非常重要的部分是首先识别错误可能会在代码中的什么地方发生。因为 JavaScript 是松散类型的,不会验证函数参数,所以很多错误只有在代码真正运行起来时才会出现。通常,需要注意 3 类错误:
     类型转换错误
     数据类型错误
     通信错误
    静态代码分析器: 不得不说的是,通过在代码构建流程中添加静态代码分析或代码检查器(linter),可以预先发现非常多的错误。常用的静态分析工具是 JSHint、JSLint、Google Closure 和 TypeScript。
    静态代码分析器要求使用类型、函数签名及其他指令来注解 JavaScript,以此描述程序如何在基本可执行代码之外运行。分析器会比较注解和 JavaScript 代码的各个部分,对在实际运行时可能出现的潜在不兼容问题给出提醒。
    类型转换错误: 类型转换错误的主要原因是使用了会自动改变某个值的数据类型的操作符或语言构造。
    数据类型错误: 因为 JavaScript 是松散类型的,所以变量和函数参数都不能保证会使用正确的数据类型。开发者需要自己检查数据类型,确保不会发生错误。数据类型错误常发生在将意外值传给函数的时候。
    通信错误: 随着 Ajax 编程的出现,Web 应用程序在运行期间动态加载数据和功能成为常见的情形。JavaScript和服务器之间的通信也会出现错误。
  • 区分重大与非重大错误: 任何错误处理策略中一个非常重要的方面就是确定某个错误是否为重大错误。具有以下一个或多个特性的错误属于非重大错误:
     不会影响用户的主要任务;
     只会影响页面中某个部分;
     可以恢复;
     重复操作可能成功。
    另一方面,重大错误具备如下特性:
     应用程序绝对无法继续运行;
     错误严重影响了用户的主要目标;
     会导致其他错误发生。
  • 把错误记录到服务器中: Web 应用程序开发中的一个常见做法是建立中心化的错误日志存储和跟踪系统。数据库和服务器错误正常写到日志中并按照常用 API 加以分类。对复杂的 Web 应用程序而言,最好也把 JavaScript 错误发送回服务器记录下来。这样做可以把错误记录到与服务器相同的系统,只要把它们归类到前端错误即可。使用相同的系统可以进行相同的分析,而不用考虑错误来源。
    要建立 JavaScript 错误日志系统,首先需要在服务器上有页面或入口可以处理错误数据。该页面只要从查询字符串中取得错误数据,然后把它们保存到错误日志中即可。比如,该页面可以使用如下代码:
    1
    2
    3
    4
    5
    6
    function logError(sev, msg) { 
    let img = new Image(),
    encodedSev = encodeURIComponent(sev),
    encodedMsg = encodeURIComponent(msg);
    img.src = 'log.php?sev=${encodedSev}&msg=${encodedMsg}';
    }
    logError()函数接收两个参数:严重程度和错误消息。严重程度可以是数值或字符串,具体取决于使用的日志系统。这里使用 Image 对象发送请求主要是从灵活性方面考虑的。
     所有浏览器都支持 Image 对象,即使不支持 XMLHttpRequest 对象也一样。
     不受跨域规则限制。通常,接收错误消息的应该是多个服务器中的一个,而 XMLHttpRequest此时就比较麻烦。
     记录错误的过程很少出错。大多数 Ajax 通信借助 JavaScript 库的包装来处理。如果这个库本身出错,而你又要利用它记录错误,那么显然错误消息永远不会发给服务器。只要是使用 try/catch 语句的地方,都可以把相关错误记录下来。下面是一个例子:
    for (let mod of mods){
    try {
    mod.init();
    } catch (ex){
    logError(“nonfatal”, ‘Module init failed: ${ex.message}’);
    }
    }
    在这个例子中,模块初始化失败就会调用 logError()函数。第一个参数是表示错误严重程度的”nonfatal”,第二个参数在上下文信息后面追加了 JavaScript 错误消息。记录到服务器的错误消息应该包含尽量多的上下文信息,以便找出错误的确切原因。
    3、调试技术: 在 JavaScript 调试器出现以前,开发者必须使用创造性的方法调试代码。结果就出现了各种各样专门为输出调试信息而设计的代码。其中最为常用的调试技术是在相关代码中插入 alert(),这种方式既费事(调试完之后还得清理)又麻烦(如果有漏洞的警告框出现在产品环境中,会给用户造成不便)。已不再推荐将警告框用于调试,因为有其他更好的解决方案。
  • 把消息记录到控制台: 所有主流浏览器都有 JavaScript 控制台,该控制台可用于查询 JavaScript 错误。另外,这些浏览器都支持通过 console 对象直接把 JavaScript 消息写入控制台,这个对象包含如下方法。
     error(message):在控制台中记录错误消息。
     info(message):在控制台中记录信息性内容。
     log(message):在控制台记录常规消息。
     warn(message):在控制台中记录警告消息。
    注意 相比于使用警告框,打印日志消息是更好的调试方法。这是因为警告框会阻塞代码执行,从而影响对异步操作的计时,进而影响代码的结果。打印日志也可以随意输出任意多个参数并检查对象实例(警告框只能将对象序列化为一个字符串再展示出来,因此经常会看到 Object[Object]。
  • 理解控制台运行时: 浏览器控制台是个读取-求值-打印-循环(REPL,read-eval-print-loop),与页面的 JavaScript 运行时并发。
  • 使用js调试器: 在所有主流浏览器中都可以使用的还有 JavaScript 调试器。ECMAScript 5.1 规范定义了 debugger关键字,用于调用可能存在的调试功能。如果没有相关的功能,这条语句会被简单地跳过。可以像下面这样使用 debugger 关键字:
    function pauseExecution(){
    console.log(“Will print before breakpoint”);
    debugger;
    console.log(“Will not print until breakpoint continues”);
    }
  • 在页面中打印消息: 另一种常见的打印调试消息的方式是把消息写到页面中指定的区域。这个区域可以是所有页面中都包含的元素,但仅用于调试目的;也可以是在需要时临时创建的元素。
  • 补充控制台方法: 记住使用哪个日志方法(原生的 console.log()和自定义的 log()方法),对开发者来说是一种负担。因为 console 是一个全局对象,所以可以为这个对象添加方法,也可以用自定义的函数重写已有的方法,这样无论在哪里用到的日志打印方法,都会按照自定义的方式行事。
  • 抛出错误: 如前所述,抛出错误是调试代码的很好方式。如果错误消息足够具体,只要看一眼错误就可以确定原因。好的错误消息包含关于错误原因的确切信息,因此可以减少额外调试的工作量。比如下面的函数:
    4、旧版IE的常见错误:
  • 无效字符
  • 未找到成员
  • 未知运行时错误
  • 语法错误
  • 系统找不到指定资源
    5、小结: 对于今天复杂的 Web 应用程序而言,JavaScript 中的错误处理十分重要。未能预测什么时候会发生错误以及如何从错误中恢复,会导致糟糕的用户体验,甚至造成用户流失。大多数浏览器默认不向用户报告 JavaScript 错误,因此在开发和调试时需要自己实现错误报告。不过在生产环境中,不应该以这种方式报告错误。
    下列方法可用于阻止浏览器对 JavaScript 错误作出反应。
     使用 try/catch 语句,可以通过更合适的方式对错误做出处理,避免浏览器处理。
     定义 window.onerror 事件处理程序,所有没有通过 try/catch 处理的错误都会被该事件处理程序接收到(仅限 IE、Firefox 和 Chrome)。
    开发 Web 应用程序时,应该认真考虑可能发生的错误,以及如何处理这些错误。
     首先,应该分清哪些算重大错误,哪些不算重大错误。
     然后,要通过分析代码预测很可能发生哪些错误。由于以下因素,JavaScript 中经常出现错误:
     类型转换;
     数据类型检测不足;
     向服务器发送错误数据或从服务器接收到错误数据。
    IE、Firefox、Chrome、Opera 和 Safari 都有 JavaScript 调试器,有的内置在浏览器中,有的是作为扩展,需另行下载。所有调试器都能够设置断点、控制代码执行和在运行时检查变量值。
此页目录
2025-04-11 日报 Day153