关注前端小讴,浏览更多原创技术文章

错误处理

相干代码 →

try/catch 语句

  • ES3 新增了try/catch语句,根本语法与 Java 中的 try/catch 一样
try {  // 可能出错的代码  const a = 3;  a = 4;} catch (error) {  // 出错时执行的代码  console.log("An error happened!"); // An error happened!}
  • try 块中有代码产生谬误,代码会立刻退出执行并跳到 catch 块中
  • 所有浏览器都反对谬误对象的messagename属性
try {  const a = 3;  a = 4;} catch (error) {  console.log(error);  /*     TypeError: Assignment to constant variable.      at Object.<anonymous> (c:\Users\43577\Desktop\工作\my_project\my_demos\javascript高级程序设计(第四版)\第21章 错误处理与调试\21.2.错误处理.js:13:5)      at Module._compile (internal/modules/cjs/loader.js:1085:14)      at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)      at Module.load (internal/modules/cjs/loader.js:950:32)      at Function.Module._load (internal/modules/cjs/loader.js:790:12)      at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:75:12)      at internal/main/run_main_module.js:17:47  */  console.log(error.name); // TypeError(类型谬误)  console.log(error.message); // Assignment to constant variable.(常量被赋值)}

finally 子句

  • try/catch 中可选的 finally 子句始终运行,二者均无奈阻止finally 块执行

    • try 中代码运行完,会执行 finally 的代码
    • 出错并执行 catch 中代码,仍会执行 finally 的代码
// finally 子句try {  console.log(1); // 1,执行} catch (error) {  console.log(2); // 不执行} finally {  console.log(3); // 3,执行}try {  const a = 3;  a = 4;} catch (error) {  console.log(2); // 2,try出错执行catch} finally {  console.log(3); // 3,仍执行}
  • 代码中蕴含 finally, try 或 catch 中的 return 会被疏忽
console.log(  (function testFinally() {    try {      console.log("try"); // try,非return语句不受影响      return 1;    } catch (error) {      return 2;    } finally {      console.log("finally"); // finally      return 3;    }  })()); // 3,蕴含finally语句,try或catch中的return会被疏忽

谬误类型

  • Error,基类型
  • InternalError,底层引擎异样时,如递归过多导致的栈溢出
  • EvalError,应用 eval()异样时,但浏览器不会总抛出 EvalError
  • RangeError,数值越界时
  • ReferenceError,找不到对象时
  • SyntaxError,给 eval()传入的字符串蕴含语法错误时
  • TypeError,最常见

    • 变量不是预期类型时
    • 拜访不存在的办法时
new Array(-1); // RangeError: Invalid array lengthlet obj = x; // ReferenceError: x is not definedeval("1++2"); // SyntaxError: Invalid left-hand side expression in postfix operationconsole.log("a" in "abc"); // TypeError: Cannot use 'in' operator to search for 'a' in abcFunction.prototype.toString().call("name"); // TypeError: Function.prototype.toString(...).call is not a function
  • 能够应用instanceof操作符在 catch 块中确定谬误类型
try {  const a = 3;  a = 4;} catch (error) {  if (error instanceof TypeError) {    console.log("TypeError!");  }} // TypeError!try {  new Array(-1);} catch (error) {  if (error instanceof RangeError) {    console.log("RangeError!");  }} // RangeError!

try/catch 的用法

  • 浏览器认为 try/catch 中产生的谬误已被解决,不会再报错
  • try/catch 最好应用在开发者无法控制有可能呈现谬误上(如不便批改代码的第三方 js 库,最好应用 try/catch 把函数调用包起来)

抛出谬误

  • throw 操作符可在任何时候抛出自定义谬误

    • throw 操作符必须有值,类型不限
    // throw 12345; // Uncaught 12345,后续代码进行// throw "Hello world"; // Uncaught Hello world,后续代码进行// throw true; // Uncaught true,后续代码进行// throw { name: "JS" }; // Uncaught {name: 'JS'},后续代码进行
    • 应用 throw 时代码立刻进行,try/catch 语句中捕捉了抛出的值时除外
    try {  throw 123;} catch (error) {  console.log(123);} // 123console.log(5); // 5,throw被try/catch捕捉,后续代码照常
  • 可通过内置谬误类型模仿浏览器谬误
// throw new SyntaxError; //  Uncaught SyntaxError// throw new InternalError; //  Uncaught InternalError// throw new TypeError; //  Uncaught TypeError// throw new RangeError; //  Uncaught RangeError// throw new EvalError; //  Uncaught EvalError// throw new URIError; //  Uncaught URIError// throw new RefenceError; //  Uncaught RefenceError
  • 可通过继承 Error创立自定义谬误类型,创立时需提供 name 和 message 属性
class CustomError extends Error {  constructor(message) {    super(message); // super调用父类构造函数,手动给父类传参,并将返回值赋给子类中的this    this.name = "CustomError";    this.message = message;  }}// throw new CustomError("My message"); // CustomError: My message

何时抛出谬误

  • 已知函数无奈正确执行时,浏览器会主动抛出谬误
  • 简单的程序很难找到谬误起因,适当创立自定义谬误可无效进步代码的可维护性
  • 应认真评估每个函数,尤其可能导致失败的情景
function process(values) {  if (!(values instanceof Array)) {    throw new Error("process(): Argument must be an Array.");  }  values.sort(); // 如果values不是数组,则浏览器会报错。因而在此句之前判断参数类型且用自定义谬误,可无效进步代码可维护性  for (let value of values) {    if (value > 100) {      return value;    }  }  return -1;}// process(1); // Error: process(): Argument must be an Array.// process(1); // TypeError: values.sort is not a function(如果没有throw代码段的后果)

抛出谬误与 try/catch

  • 捕捉谬误的目标是阻止浏览器以其默认形式响应
  • 抛出谬误的目标是提供无关其产生起因的阐明
  • 应该在明确接下来做什么时捕捉谬误

error 事件

  • 没有被 try/catch 捕捉的谬误会在浏览器 window 对象上触发 error 事件

    • onerror 事件处理程序中,任何浏览器都不传入 event 对象
    • 传入 3 个参数:谬误音讯、产生谬误的 URL、产生谬误的行号
    • 任何谬误产生都会触发 error 事件,并执行事件的处理程序,浏览器默认行为会失效
    • 能够返回 false 来阻止浏览器默认报告谬误的行为
window.onerror = (message, url, line) => {  console.log(message);  return false; // 阻止浏览器默认报告谬误};
  • 图片中 src 属性的 url 没有返回可辨认的图片格式,也会触发 error 事件
const image = new Image();image.addEventListener("load", (event) => {  console.log("Image loaded!");});image.addEventListener("error", (event) => {  console.log("Image not loaded!");});image.src = "a.jpg"; // Image not loaded!

辨认谬误

类型转换谬误

  • 次要起因是应用了会主动扭转某个值的数据类型的草错付或语言结构

    • 比拟过程中,应应用严格相等严格不等防止谬误
    console.log(5 == "5"); // trueconsole.log(5 === "5"); // false,数据类型不同console.log(1 == true); // trueconsole.log(1 === true); // false,数据类型不同
    • 在 if、for、while 等流程管制语句中,应保持应用布尔值作为条件防止谬误
    function concat(str1, str2, str3) {  let result = str1 + str2;  if (str3) {    result += str3;  }  return result;}console.log(concat("1", "2", "0")); // '120'console.log(concat("1", "2")); // '12',str3是undifined,转化为falseconsole.log(concat("1", "2", 0)); // '12',str3是数值0,转化为false,与预期不符function concat(str1, str2, str3) {  let result = str1 + str2;  if (str3 !== undefined) {    result += str3;  }  return result;}console.log(concat("1", "2", "03")); // '120'console.log(concat("1", "2")); // '12',str3 是 undifined,转化为 falseconsole.log(concat("1", "2", 0)); // '120',达到预期

数据类型谬误

  • JS 是涣散类型,其变量函数参数不能保障数据类型

    • 原始类型的值,应用typeof检测
    function getQueryString(url) {  const pos = url.indexOf("?"); // indexOf是字符串才有的办法  if (pos > 1) {    console.log(url.substring(pos + 1)); // substring是字符串才有的办法    return;  }  console.log("not has ?");}// getQueryString(123); // TypeError: url.indexOf is not a functionfunction getQueryString2(url) {  if (typeof url === "string") {    // 确保不会因为参数是非字符串值而报错    const pos = url.indexOf("?");    if (pos > 1) {      console.log(url.substring(pos + 1));      return;    }    console.log("not has ?");  }}getQueryString2(123); // 不打印getQueryString2("123"); // 'not has ?'getQueryString2("https://www.baidu.com?keyWord=error"); // 'keyWord=error'
    • 对象值,应用instanceof检测
    function reverseSort(values) {  if (values) {    // 不可取,values为true的状况很多    values.sort();    values.reverse();  }}// reverseSort(1); // TypeError: values.sort is not a functionfunction reverseSort2(values) {  if (values !== null) {    // 不可取,values不为null的状况很多    values.sort();    values.reverse();  }}// reverseSort2(1); // TypeError: values.sort is not a functionfunction reverseSort3(values) {  if (typeof values.sort === "function") {    // 不可取,如果values有sort()办法但不是数组则会报错    values.sort();    values.reverse();  }}// reverseSort3({//   sort: () => {//     console.log("3");//   },// }); // 先values.sort()打印3,后报错TypeError: values.reverse is not a functionfunction reverseSort4(values) {  if (values instanceof Array) {    // 可取,确保values是Array的实例    values.sort();    values.reverse();  }}let val1 = 1;let val2 = [1, 2];reverseSort4(val1);reverseSort4(val2);console.log(val1); // 1console.log(val2); // [2,1]

通信谬误

  • 对于 url 的查问字符串,都要通过encodeURIComponent(),以确保编码适合
let url = "https://www.baidu.com?keyWord=https://www.taobao.com"; // url格局不正确function addQueryStringArg(url, name, value) {  if (url.indexOf("?") === -1) {    url += "?";  } else {    url += "&";  }  url += `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;  return url;}let url2 = addQueryStringArg(  "https://www.baidu.com",  "keyWord",  "https://www.taobao.com");console.log(url2); // https://www.baidu.com?keyWord=https%3A%2F%2Fwww.taobao.com,与服务器通信的正确url格局

辨别重大与非重大谬误

  • 非重大谬误

    • 不影响用户次要工作
    • 只影响页面中某个局部
    • 可复原
    • 反复操作可能胜利
  • 重大谬误

    • 程序无奈持续运行
    • 重大影响用户的次要指标
    • 会导致其余谬误
  • 程序某个局部的谬误,不应该影响其余局部
  • 模块初始化时,可在 for 循环中退出 try/catch 语句,防止某一模块初始化时产生谬误影响其余模块
let mods = [  {    name: "mod1",    init: () => {      const a = 1;      a = 2;      console.log("mod1 init");    }, // mod1的init办法里有谬误  },  {    name: "mod2",    init: () => {      console.log("mod2 init");    },  },];for (let mod of mods) {  // mod.init(); // 不好,只有有一个mod的init办法出错,影响后续  try {    mod.init(); // 'mod2 init',mod2照常运行  } catch (error) {    console.log(error); // TypeError: Assignment to constant variable.  }}

总结 & 问点

  • 谬误对象中的哪些属性在全副浏览器中都向用户显示?
  • finally 对 try 或 catch 中的非 return 语句和 return 语句别离有什么影响?
  • 请举例说明有哪些常见谬误类型及其呈现的起因
  • 请写一段代码,在 try/catch 块中,确定谬误的类型
  • throw 操作符必须有值嘛?须要什么数据类型的值?如何能力既应用该操作符又不影响后续代码执行?
  • 写一段代码,通过继承 Error 创立一个自定义的谬误类型,创立其实例并用 throw 将其抛出
  • 写一段代码,在一个函数里,通过创立自定义谬误类型进步其可维护性
  • 常见的类型转换谬误有哪些?别离该如何防止呢?
  • 应别离怎么检测,以防止原始值和对象值在函数传参时可能产生的数据类型谬误?
  • 写一个办法,解决与服务器通信的 url 的正确格局
  • 写一段代码,用 for 循环模仿模块初始化,某一模块加载时产生谬误但不影响后续模块