Как создать пользовательскую ошибку в JavaScript? - PullRequest
192 голосов
/ 24 апреля 2009

Почему-то похоже, что делегирование конструктора не работает в следующем фрагменте:

function NotImplementedError() { 
  Error.apply(this, arguments); 
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")

Запуск этого дает The message is: ''. Есть идеи, почему, или есть лучший способ создать новый подкласс Error? Есть ли проблема с apply в нативном конструкторе Error, о котором я не знаю?

Ответы [ 20 ]

1 голос
/ 27 января 2014

Это реализовано в Cesium DeveloperError:

В упрощенном виде:

var NotImplementedError = function(message) {
    this.name = 'NotImplementedError';
    this.message = message;
    this.stack = (new Error()).stack;
}

// Later on...

throw new NotImplementedError();
1 голос
/ 24 апреля 2009

Конструктор должен быть похож на фабричный метод и возвращать то, что вы хотите. Если вам нужны дополнительные методы / свойства, вы можете добавить их к объекту перед его возвратом.

function NotImplementedError(message) { return new Error("Not implemented", message); }

x = new NotImplementedError();

Хотя я не уверен, зачем тебе это нужно. Почему бы просто не использовать new Error...? Пользовательские исключения на самом деле мало что добавляют в JavaScript (или, возможно, в любом нетипизированном языке).

1 голос
/ 12 сентября 2018

Это моя реализация:

class HttpError extends Error {
  constructor(message, code = null, status = null, stack = null, name = null) {
    super();
    this.message = message;
    this.status = 500;

    this.name = name || this.constructor.name;
    this.code = code || `E_${this.name.toUpperCase()}`;
    this.stack = stack || null;
  }

  static fromObject(error) {
    if (error instanceof HttpError) {
      return error;
    }
    else {
      const { message, code, status, stack } = error;
      return new ServerError(message, code, status, stack, error.constructor.name);
    }
  }

  expose() {
    if (this instanceof ClientError) {
      return { ...this };
    }
    else {
      return {
        name: this.name,
        code: this.code,
        status: this.status,
      }
    }
  }
}

class ServerError extends HttpError {}

class ClientError extends HttpError { }

class IncorrectCredentials extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 400;
  }
}

class ResourceNotFound extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 404;
  }
}

Пример использования # 1:

app.use((req, res, next) => {
  try {
    invalidFunction();
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

Пример использования # 2:

router.post('/api/auth', async (req, res) => {
  try {
    const isLogged = await User.logIn(req.body.username, req.body.password);

    if (!isLogged) {
      throw new IncorrectCredentials('Incorrect username or password');
    }
    else {
      return res.status(200).send({
        token,
      });
    }
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});
0 голосов
/ 22 сентября 2018

Мне нравится делать это так, чтобы сообщение было одинаковым в трассировке стека или в toString, и я мог передать только имя или имя и сообщение. Полезно, например, при выдаче ошибок HTTP, ваши обработчики могут просто error.toString() передать пользователю, и он будет элегантно обрабатывать ваши ошибки или любые другие.

class AppException extends Error {
  constructor(code, message) {
    const fullMsg = message ? `${code}: ${message}` : code;
    super(fullMsg);
    this.name = code;
    this.message = fullMsg;
  }
  
  toString() {
    return this.message;
  }
}

// Just an error name
try {
  throw new AppException('Forbidden');
} catch(e) {
  console.error(e);
  console.error(e.toString());
}

// A name and a message
try {
  throw new AppException('Forbidden', 'You don\'t have access to this page');
} catch(e) {
  console.error(e);
  console.error(e.toString());
}
0 голосов
/ 05 сентября 2017

MDN имеет отличный пример :

try {
  throw new Error('Whoops!');
} catch (e) {
  console.log(e.name + ': ' + e.message);
}
0 голосов
/ 20 августа 2012

Попробуйте новый объект-прототип для каждого экземпляра определенного пользователем типа ошибки. Это позволяет instanceof проверкам вести себя как обычно, плюс тип и сообщение правильно передаются в Firefox и V8 (Chome, nodejs).

function NotImplementedError(message){
    if(NotImplementedError.innercall===undefined){
        NotImplementedError.innercall = true;
        NotImplementedError.prototype = new Error(message);
        NotImplementedError.prototype.name = "NotImplementedError";
        NotImplementedError.prototype.constructor = NotImplementedError;

        return new NotImplementedError(message);
    }
    delete NotImplementedError.innercall;
}

Обратите внимание, что перед в противном случае правильным стеком будет дополнительная запись.

0 голосов
/ 06 марта 2013

более простой способ. Вы можете сделать свой объект наследуемым от объекта Error. Пример:

function NotImplementError(message)
{
    this.message = message;
    Error.call();
    Error.call(message);
} 

то, что мы делаем, - это использование функции call (), которая вызывает конструктор класса Error, поэтому это то же самое, что и реализация наследования классов в других объектно-ориентированных языках.

0 голосов
/ 28 апреля 2013

За счет невозможности использовать instanceof, следующее сохраняет исходную трассировку стека и не использует никаких нестандартных приемов.

// the function itself
var fixError = function(err, name) {
    err.name = name;
    return err;
}

// using the function
try {
    throw fixError(new Error('custom error message'), 'CustomError');
} catch (e) {
    if (e.name == 'CustomError')
        console.log('Wee! Custom Error! Msg:', e.message);
    else
        throw e; // unhandled. let it propagate upwards the call stack
}
0 голосов
/ 30 июля 2013

Другая альтернатива, может работать не во всех средах. По крайней мере, заверил, что она работает в nodejs 0.8 В этом подходе используется нестандартный способ изменения внутреннего прототипа

function myError(msg){ 
      var e = new Error(msg); 
      _this = this; 
      _this.__proto__.__proto__ = e;
}
0 голосов
/ 25 октября 2014

Если вы используете Node / Chrome. Следующий фрагмент даст вам расширение, которое соответствует следующим требованиям.

  • err instanceof Error
  • err instanceof CustomErrorType
  • console.log () возвращает [CustomErrorType] при создании с сообщением
  • console.log () возвращает [CustomErrorType: message] при создании без сообщения
  • throw / stack предоставляет информацию в тот момент, когда была создана ошибка.
  • Оптимально работает в Node.JS и Chrome.
  • Пройдет проверку экземпляра в Chrome, Safari, Firefox и IE 8+, но не будет иметь действительный стек вне Chrome / Safari. Я в порядке с этим, потому что я могу отлаживать в Chrome, но код, который требует определенных типов ошибок, будет по-прежнему работать через браузер. Если вам нужен только Node, вы можете легко удалить операторы if, и вы готовы пойти .

Отрывок

var CustomErrorType = function(message) {
    if (Object.defineProperty) {
        Object.defineProperty(this, "message", {
            value : message || "",
            enumerable : false
        });
    } else {
        this.message = message;
    }

    if (Error.captureStackTrace) {
        Error.captureStackTrace(this, CustomErrorType);
    }
}

CustomErrorType.prototype = new Error();
CustomErrorType.prototype.name = "CustomErrorType";

Использование

var err = new CustomErrorType("foo");

выход

var err = new CustomErrorType("foo");
console.log(err);
console.log(err.stack);

[CustomErrorType: foo]
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

/errorTest.js:30
        throw err;
              ^
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...