前端异常的捕获与处理

哈喽大家好,今天的原创文章由团队的uncle13老师提供。

异常,即程序运行过程中出现的预料之外的事件,它们往往导致程序无法按预期执行。例如,页面元素出现异常(如按钮点击无响应、元素不显示)、页面卡顿或白屏等,这些情况都极大地影响了用户的体验。对于前端而言,尽管异常不会直接导致计算机崩溃,但用户的操作却可能被阻断,从而影响他们对产品的整体印象。因此,理解异常、学习处理异常,对于我们前端开发者来说至关重要。

1,异常分类

异常本质上是一个数据结构,用于存储异常发生时的相关信息,如错误码、错误信息等。其中,message属性是所有浏览器都支持的唯一属性。不同浏览器还提供了其他相关属性,如IE添加了description属性和number属性,Firefox则添加了fileName、lineNumber和stack等属性。因此,在编写跨浏览器代码时,为确保兼容性,最好仅使用message属性。

在JavaScript执行过程中,可能会遇到多种类型的错误。每种错误都有对应的错误类型,当错误发生时,会抛出相应的错误对象。ECMAScript规范定义了以下7种错误类型:

  • Error:所有错误的基类,其他错误类型均继承自它。
  • EvalError:eval()函数执行时产生的错误。
  • RangeError:数值或索引超出其有效范围时发生的错误,如数组越界。
  • ReferenceError:尝试引用一个未定义的变量时抛出的错误。
  • SyntaxError:代码语法解析错误,如语法结构不正确。
  • TypeError:当值的类型不是预期类型时抛出的错误,如将一个字符串当作数字使用。
  • URIError:全局URI处理函数使用不当导致的错误,如编码和解码URI时出现问题。

2,异常处理

2.1,异常捕获

在JavaScript中,可以使用try...catch语句来捕获异常。try块包含可能抛出异常的代码,而catch块则用于处理这些异常。

try {
  // 尝试执行可能抛出异常的代码
  let result = someFunctionThatMightThrow();
catch (error) {
  // 处理异常
  console.error('捕获到异常:', error);
  // 可以根据需要进行错误恢复或记录等操作
}

2.2,错误对象

当异常被抛出时,会创建一个错误对象。这个对象通常包含关于错误的详细信息,如错误类型、消息和堆栈跟踪。我们可以利用这些信息来定位问题并采取相应的处理措施。

try {
  // ...
catch (error) {
  console.error('错误类型:', error.name);
  console.error('错误信息:', error.message);
  console.error('堆栈跟踪:', error.stack);
}

2.3,错误类型处理

常见js错误:

1, 语法错误(SyntaxError)
// 缺少分号
var message = "Hello, world"
console.log(message);

// 错误的引号使用
var greeting = 'Hello, world;
console.log(greeting);

// 错误的变量名(不能包含特殊字符或保留字)
var 123name = "John";

// 函数定义缺少圆括号
function myFunction {
  console.log("This is a function");
}
2,类型错误(TypeError)
// 尝试调用一个非函数
var number = 123;
number(); // TypeError: number is not a function

// 访问未定义的属性
var obj = {};
console.log(obj.nonexistentProperty); // undefined (但如果严格模式,会抛出TypeError)

// 将null或undefined当作对象使用
var obj = null;
console.log(obj.property); // TypeError: Cannot read property 'property' of null
3, 引用错误(ReferenceError)
// 引用一个未声明的变量
console.log(undeclaredVariable); // ReferenceError: undeclaredVariable is not defined

// 尝试删除一个不可删除的属性
delete window.location; // ReferenceError: attempt to delete unconfigurable property 'location'
4, 范围错误(RangeError)
// 数组长度超出限制
var longArray = new Array(-1); // RangeError: Invalid array length

// 数字超出范围
var bigNumber = Number.MAX_SAFE_INTEGER + 1;
console.log(bigNumber); // 输出可能不准确,但不一定会抛出RangeError

// 字符串超出长度限制
var longString = 'a'.repeat(Number.MAX_SAFE_INTEGER + 1); // RangeError: Invalid count value


根据异常的类型,可以进行不同的处理。例如,对于网络请求失败,我们可能希望重试请求或显示一个友好的错误消息;对于类型错误,我们可能需要进行类型转换或验证。

```javascript
try {
  // ...
} catch (error) {
  if (error instanceof TypeError) {
    // 处理类型错误
  } else if (error instanceof ReferenceError) {
    // 处理引用错误
  } else {
    // 处理其他类型的错误
  }
}

2.4,全局异常处理

除了使用try...catch语句外,我们还可以监听全局异常事件,如window.onerrorwindow.addEventListener('unhandledrejection', ...)来处理未捕获的异常或Promise拒绝。

window.onerror = function(message, source, lineno, colno, error{
  // 处理全局未捕获异常
  console.error('全局异常:', message, '在', source, '的', lineno, '行', colno, '列');
};

window.addEventListener('unhandledrejection'function(event{
  // 处理未处理的Promise拒绝
  console.error('未处理的Promise拒绝:', event.reason);
});

3, 异常捕获

3.1, 使用 try-catch 语句

try-catch 是JavaScript中用于捕获和处理异常的标准方法。当try块中的代码抛出异常时,catch块会捕获该异常并允许你处理它。

try {
    // 尝试执行的代码
    let x = y; // y未定义,这里会抛出异常
catch (error) {
    // 处理异常
    console.error("捕获到异常:", error);
}

3.2, 使用 window.onerror

window.onerror 是一个全局错误处理函数,当在全局作用域中发生未捕获的异常时,它会被调用。这可以用于捕获那些没有被try-catch块覆盖的异常。

window.onerror = function(message, source, lineno, colno, error{
    console.error('捕获到全局异常:', message, '在', source, '的', lineno, '行, 第', colno, '列', error);
    return false// 返回 false 以阻止默认的错误处理
};

3.3, 使用 Promise 的 .catch() 方法

当你使用Promise进行异步操作时,可以使用.catch()方法来捕获和处理可能发生的异常。

// 返回一个Promise的异步函数
function asyncOperation({
  return new Promise((resolve, reject) => {
    setTimeout(function({
      // 模拟异步操作失败
      reject(new Error('异步操作失败'));
    }, 1000);
  });
}

// 使用Promise的.then和.catch处理结果和错误
asyncOperation()
  .then(result => {
    // 处理结果
    console.log('异步操作成功:', result);
  })
  .catch(error => {
    // 处理错误
    console.error('异步错误:', error.message);
  });

3.4, 使用 async/await 与 try-catch

当使用async/await进行异步操作时,也可以结合try-catch来捕获和处理异常。

// 返回一个Promise的异步函数
function asyncOperation({
  return new Promise((resolve, reject) => {
    setTimeout(function({
      // 模拟异步操作失败
      reject(new Error('异步操作失败'));
    }, 1000);
  });
}

// 使用async/await和try/catch处理结果和错误
async function main({
  try {
    const result = await asyncOperation();
    // 处理结果
    console.log('异步操作成功:', result);
  } catch (error) {
    // 处理错误
    console.error('异步错误:', error.message);
  }
}

// 调用main函数
main();

3.5, 使用错误边界 (Error Boundaries) 在 React 中

在React中,错误边界是一种React组件,它可以捕获并打印发生在其子组件树任何位置的JavaScript错误,并且,它会阻止错误冒泡至更高层。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasErrorfalse };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasErrortrue };
  }

  componentDidCatch(error, errorInfo) {
    // 你同样可以将错误日志上报给服务器
    console.error(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 你可以自定义降级后的 UI 并渲染
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

3.6, Vue中相关异常处理

在Vue中,异常处理通常涉及几个层面:全局异常处理、组件内异常处理以及针对异步操作(如API请求)的异常处理。

3.6.1 全局异常处理

Vue本身没有提供全局异常处理的内置机制,可以通过全局事件监听、Vuex或Vue的生命周期钩子来实现类似的功能。

一种常见的做法是在main.js或应用的入口文件中设置一个全局错误处理函数,并监听Vue实例的errorCapturederrorHandler钩子。

import Vue from 'vue';
import App from './App.vue';

Vue.config.errorHandler = function (err, vm, info{
  // 处理错误
  console.error('捕获到全局异常:', err, info);
};

new Vue({
  renderh => h(App),
  mounted() {
    window.addEventListener('error'this.globalErrorHandler);
  },
  beforeDestroy() {
    window.removeEventListener('error'this.globalErrorHandler);
  },
  methods: {
    globalErrorHandler(event) {
      // 处理全局窗口错误
      console.error('全局窗口错误:', event);
    }
  }
}).$mount('#app');
3.6.2, 组件内异常处理

在Vue组件中,可以使用errorCaptured钩子来捕获并处理子组件中的错误。

export default {
  errorCaptured(err, vm, info) {
    // 捕获到子组件的错误
    console.error('子组件异常:', err, info);
    // 返回false,阻止错误继续向上冒泡
    return false;
  }
}
3.6.3, 异步操作异常处理

对于异步操作,如API请求,应该在相应的回调或Promise的.catch()块中处理异常。

使用async/await时:

async function fetchData({
  try {
    const response = await fetch('/some-url');
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('捕获到 fetch 异常:', error);
    // 可以选择抛出错误或进行其他处理
  }
}

使用Promise时:

fetch('/some-url')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(data => {
    // 处理数据
  })
  .catch(error => {
    console.error('捕获到 fetch 异常:', error);
  });
3.6.4,使用Vuex进行状态管理时的异常处理

在Vue应用中使用Vuex进行状态管理,可以在actions中处理异步操作的异常,并通过mutations或getters将这些异常状态反映到组件中。


最后

还没有使用过我们刷题网站(https://fe.ecool.fun/)或者前端面试题宝典的同学,如果近期准备或者正在找工作,千万不要错过,我们的题库主打无广告和更新快哦~。

老规矩,也给我们团队的辅导服务打个广告。