在JavaScript中自定义错误的详细教程

118 阅读2分钟

在JavaScript中,有两种错误处理的情况。一种是由第三方抛出的错误(如库、数据库、API),另一种是你想自己抛出错误。对于前者,你有错误在手,而对于后者,你需要自己新建:

function throwAnError() {
  throw new Error('Something went wrong.');
}

try {
  throwAnError();
} catch (error) {
  console.log(error.message); // 'Something went wrong.'
  console.log(error.name); // Error
}

但有时你想抛出自定义错误。我了解到,你可以通过以下方式创建自定义错误。如果你必须自己创建错误,那就为它创建一个新的自定义错误类,该类扩展自本地JavaScript错误。在那里你可以设置作为参数的错误信息,模拟本地错误类的API,并设置一个自定义的错误名称:

class BadRequestError extends Error {
  constructor(message) {
    super(message);

    this.name = 'BadRequestError';
  }
}

function throwAnError() {
  throw new BadRequestError('Something went wrong.');
}

try {
  throwAnError();
} catch (error) {
  console.log(error.message); // 'Something went wrong.'
  console.log(error.name); // BadRequestError
}

你可以用更多的属性来重载这个新的自定义错误。例如,如果自定义错误必须发生在REST API层面,我可能想给它一个HTTP状态代码,我可以返回给我的用户:

class BadRequestError extends Error {
  constructor(message) {
    super(message);

    this.name = 'BadRequestError';
    this.statusCode = 400;
  }
}

function throwAnError() {
  throw new BadRequestError('Something went wrong.');
}

try {
  throwAnError();
} catch (error) {
  console.log(error.message); // 'Something went wrong.'
  console.log(error.name); // BadRequestError
  console.log(error.statusCode); // 400
}

现在,如果你不想创建一个新的错误,而是继承一个源自第三方(如数据库或库)的错误对象,会发生什么?例如,下面的数据库请求抛出了一个错误:

async function findUserById(id) {
  try {
    return await database.getUserById(id);
  } catch (error) {
    return error;
  }
};

这在大多数情况下可能没有问题,但在某些情况下,比如REST API发生这种情况,我可能想用HTTP状态码为我的服务器中间件定制错误。然后,我为它创建一个自定义的错误类,从本地错误中扩展出来,并传入第三方错误的所有属性以及我的其他信息:

export class BadRequestError extends Error {
  constructor(error) {
    super(error.message);

    this.data = { error };
    this.statusCode = 400;
  }
}

async function findUserById(id) {
  try {
    return await database.getUserById(id);
  } catch (error) {
    return new BadRequestError(error);
  }
};

这就是我如何从一个已经来自其他地方的错误中扩展。毕竟,最后的例子已经涵盖了两种情况:从头开始抛出一个新的自定义错误和自定义一个来自其他地方的错误。