Как создать собственную ошибку в JavaScript?

226

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

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конструктором, о которой я не знаю?

cdleary
источник
Работает ли утверждение nie instanceof NotImplementedError после внесения изменений? Я думал, что для этого вам нужно явно определить NotImplementedError.prototype.constructor.
jayarjo 09
В следующий раз удалите, пожалуйста, весь посторонний код, который не требуется для демонстрации вашей проблемы. Кроме того, wtc - это js.jar? Это нужно для воспроизведения проблемы?
BT
2
Отредактировал этот вопрос так, чтобы его можно было понять за 10 секунд, а не за 10 минут
BT
Я создал библиотеку наследования / классов, которая правильно наследует от типов ошибок: github.com/fresheneesz/proto
BT
1
jsfiddle, чтобы найти несколько основных ответов.
Nate

Ответы:

202

Обновите свой код, чтобы присвоить прототипу Error.prototype, а instanceof и ваши утверждения работают.

function NotImplementedError(message) {
    this.name = "NotImplementedError";
    this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

Однако я бы просто бросил ваш собственный объект и просто проверил свойство name.

throw {name : "NotImplementedError", message : "too lazy to implement"}; 

Редактировать на основе комментариев

После просмотра комментариев и попыток вспомнить, почему я назначил прототип Error.prototypeвместо того, new Error()как это сделал Николас Закас в своей статье , я создал jsFiddle с приведенным ниже кодом:

function NotImplementedError(message) {
  this.name = "NotImplementedError";
  this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

function NotImplementedError2(message) {
  this.message = (message || "");
}
NotImplementedError2.prototype = new Error();

try {
  var e = new NotImplementedError("NotImplementedError message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

try {
  var e = new NotImplementedError2("NotImplementedError2 message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

Консольный вывод был таким.

undefined
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Error
    at window.onload (http://fiddle.jshell.net/MwMEJ/show/:29:34)
ex1 instanceof NotImplementedError2 = true
ex1 instanceof Error = true
ex1.name = Error
ex1.message = NotImplementedError2 message

Это подтверждает, что «проблема», с которой я столкнулся, заключалась в том, что свойство стека ошибки было номером строки, в которой new Error()была создана, а не где она throw eпроизошла. Однако это может быть лучше, чем иметь побочный эффект NotImplementedError.prototype.name = "NotImplementedError"строки, влияющей на объект Error.

Также обратите внимание NotImplementedError2: когда я не устанавливаю .nameявно, он равен «Ошибка». Однако, как упоминалось в комментариях, поскольку эта версия устанавливает прототип new Error(), я могу установить NotImplementedError2.prototype.name = "NotImplementedError2"и быть в порядке.

Кевин Хакансон
источник
45
Лучший ответ, но брать Error.prototypeнапрямую - это, вероятно, плохой тон. Если позже вы захотите добавить NotImplementedError.prototype.toStringк объекту псевдонимы Error.prototype.toString- лучше сделать NotImplementedError.prototype = new Error().
cdleary 01
4
Я все еще немного потерялся во всех этих прототипах. Почему в вашем примере вы назначаете имя this.name, а не NotImplementedError.prototype.name? Не могли бы вы ответить, это важно для моего понимания :)
jayarjo 09
27
Согласно code.google.com/p/chromium/issues/detail?id=228909 subclass.prototype = new Error() это дурной тон . Вы должны использовать subclass.prototype = Object.create(superclass.prototype)вместо этого. Я надеюсь, что это также может решить проблему трассировки стека.
Gili
8
Простой способ получить осмысленную трассировку стека - это сгенерировать ошибку в конструкторе и сохранить его стек. Это даст правильный стек вызовов + 1 строка для конструктора (это подходящая выплата):this.stack = new Error().stack;
Meredian
6
-1; это не правильно. Делать NotImplementedError.prototype = Error.prototype;не делает instanceofлакомство в NotImplementedErrorкачестве подкласса из Error, это instanceofрассматривать их как точно такой же класс. Если вы вставите приведенный выше код в свою консоль и попытаетесь new Error() instanceof NotImplementedErrorполучить true, что явно неверно.
Марк Эмери
87

Все вышеперечисленные ответы ужасно ужасны - правда. Даже тот, у которого 107 ИБП! Настоящий ответ здесь, ребята:

Наследование от объекта Error - где находится свойство сообщения?

TL; DR:

A. Причина messageне является набор является то , что Errorэто функция , которая возвращает новый объект Error и не не манипулировать thisкаким - либо образом.

Б. Способ сделать это правильно - вернуть результат применения из конструктора, а также установить прототип обычным сложным способом javascripty:

function MyError() {
    var temp = Error.apply(this, arguments);
    temp.name = this.name = 'MyError';
    this.message = temp.message;
    if(Object.defineProperty) {
        // getter for more optimizy goodness
        /*this.stack = */Object.defineProperty(this, 'stack', { 
            get: function() {
                return temp.stack
            },
            configurable: true // so you can change it if you want
        })
    } else {
        this.stack = temp.stack
    }
}
//inherit prototype using ECMAScript 5 (IE 9+)
MyError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: MyError,
        writable: true,
        configurable: true
    }
});

var myError = new MyError("message");
console.log("The message is: '" + myError.message + "'"); // The message is: 'message'
console.log(myError instanceof Error); // true
console.log(myError instanceof MyError); // true
console.log(myError.toString()); // MyError: message
console.log(myError.stack); // MyError: message \n 
// <stack trace ...>


 
//for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code:
/*
var IntermediateInheritor = function() {};
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
*/

Вероятно, вы могли бы проделать некоторые уловки, чтобы перечислить все неперечислимые свойства tmpошибки, чтобы установить их, а не явно устанавливать только stackи message, но обман не поддерживается в ie <9

BT
источник
2
Это решение также работает для создания экземпляра пользовательской ошибки с существующей ошибкой. Если вы используете стороннюю библиотеку и хотите обернуть существующую ошибку своим собственным типом, другие методы не работают должным образом. К вашему сведению, вы можете создать экземпляр ванильных ошибок, передав им существующую ошибку.
Кайл Мюллер
1
вы не должны return thisв конструкторе.
Онур Йылдырым
13
Я немного упростил и улучшил этот подход: jsbin.com/rolojuhuya/1/edit?js,console
Мэтт Кантор
3
@MattKantor, может быть, это ответ? Думаю, мне нравится твое больше всего.
mpoisot
2
Вместо этого temp.name = this.name = 'MyError'вы можете сделать temp.name = this.name = this.constructor.name. Таким образом, это будет работать и для подклассов MyError.
Джо Лисс
56

В ES2015 вы можете использовать classдля этого:

class NotImplemented extends Error {
  constructor(message = "", ...args) {
    super(message, ...args);
    this.message = message + " has not yet been implemented.";
  }
}

Это не изменяет глобальный Errorпрототип, позволяет настраивать message, nameи другие атрибуты, а также должным образом фиксирует стек. Кроме того, это довольно удобно для чтения.

Конечно, вам может понадобиться инструмент, например, babelесли ваш код будет работать в старых браузерах.

крысоловка
источник
23

Если кому-то интересно, как создать собственную ошибку и получить трассировку стека:

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || '';
  var error = new Error(this.message);
  error.name = this.name;
  this.stack = error.stack;
}
CustomError.prototype = Object.create(Error.prototype);

try {
  throw new CustomError('foobar');
}
catch (e) {
  console.log('name:', e.name);
  console.log('message:', e.message);
  console.log('stack:', e.stack);
}
Ристо
источник
7

Этот раздел стандарта может объяснить, почему Error.applyвызов не инициализирует объект:

15.11.1 Конструктор ошибок, вызываемый как функция

Когда Error вызывается как функция, а не как конструктор, он создает и инициализирует новый объект Error. Таким образом, вызов функции Error (...) эквивалентен выражению создания объекта new Error (...) с теми же аргументами.

В этом случае Errorфункция, вероятно, определяет, что она не вызывается как конструктор, поэтому она возвращает новый экземпляр Error, а не инициализирует thisобъект.

Кажется, что тестирование с помощью следующего кода демонстрирует, что на самом деле происходит именно это:

function NotImplementedError() { 
   var returned = Error.apply(this, arguments);
   console.log("returned.message = '" + returned.message + "'");
   console.log("this.message = '" + this.message + "'");
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");

При запуске создается следующий вывод:

returned.message = 'some message'
this.message = ''
Дэйв
источник
как это можно смоделировать с помощью специального класса ошибок? Например, как мой собственный класс ошибок можно использовать как функцию, создающую экземпляр, и как конструктор?
Lea Hayes
Нет, это неправда. Если он вернул новый экземпляр Error, то его свойство msg будет работать.
BT
@BT Как свойство msg в новом экземпляре влияет на свойство msg в thisin Error.apply(this, arguments);? Я говорю, что вызов Error здесь создает новый объект, который выбрасывается; не инициализирует уже созданный объект, которому назначен nie.
Дэйв
@BT Я добавил пример кода, который, надеюсь, проясняет то, что я пытался сказать.
Dave
@Dave Я, возможно, неправильно понял цель здесь, но разве ваша NotImplementedErrorреализация не должна возвращать returnedпеременную?
blong
7
function InvalidValueError(value, type) {
    this.message = "Expected `" + type.name + "`: " + value;
    var error = new Error(this.message);
    this.stack = error.stack;
}
InvalidValueError.prototype = new Error();
InvalidValueError.prototype.name = InvalidValueError.name;
InvalidValueError.prototype.constructor = InvalidValueError;
Пьер Вуазен
источник
3
Это лучший ответ здесь. Это кратко, и созданное таким образом исключение будет правильно вести себя во всех ситуациях. Он также сохраняет трассировку стека, что очень важно для нетривиальных приложений. Я бы только заменил «prototype = new Error ()» на «prototype = Object.create (Error.prototype)». Для Node.js есть небольшая библиотека, которая сделает это за вас: npmjs.com/package/node-custom-errors
Лукаш Коржибски,
6

У меня была аналогичная проблема. Моя ошибка должна быть instanceofкак Errorи NotImplemented, так и для последовательной обратной трассировки в консоли.

Мое решение:

var NotImplemented = (function() {
  var NotImplemented, err;
  NotImplemented = (function() {
    function NotImplemented(message) {
      var err;
      err = new Error(message);
      err.name = "NotImplemented";
      this.message = err.message;
      if (err.stack) this.stack = err.stack;
    }
    return NotImplemented;
  })();
  err = new Error();
  err.name = "NotImplemented";
  NotImplemented.prototype = err;

  return NotImplemented;
}).call(this);

// TEST:
console.log("instanceof Error: " + (new NotImplemented() instanceof Error));
console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented));
console.log("message: "+(new NotImplemented('I was too busy').message));
throw new NotImplemented("just didn't feel like it");

Результат работы с node.js:

instanceof Error: true
instanceof NotImplemented: true
message: I was too busy

/private/tmp/t.js:24
throw new NotImplemented("just didn't feel like it");
      ^
NotImplemented: just didn't feel like it
    at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13)
    at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:487:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

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

зловещий бурундук
источник
6

Согласно Joyent, вам не следует связываться со свойством стека (которое я вижу во многих приведенных здесь ответах), потому что это отрицательно скажется на производительности. Вот что они говорят:

стек: как правило, не связывайтесь с этим. Даже не увеличивайте его. V8 вычисляет его только в том случае, если кто-то на самом деле читает свойство, что значительно повышает производительность для обрабатываемых ошибок. Если вы читаете свойство только для того, чтобы увеличить его, вы в конечном итоге оплатите стоимость, даже если вызывающему абоненту стек не нужен.

Я хотел бы упомянуть их идею обертывания исходной ошибки, которая является хорошей заменой для передачи в стек.

Итак, вот как я создаю настраиваемую ошибку, учитывая вышесказанное:

версия es5:

function RError(options) {
    options = options || {}; // eslint-disable-line no-param-reassign
    this.name = options.name;
    this.message = options.message;
    this.cause = options.cause;

    // capture stack (this property is supposed to be treated as private)
    this._err = new Error();

    // create an iterable chain
    this.chain = this.cause ? [this].concat(this.cause.chain) : [this];
}
RError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: RError,
        writable: true,
        configurable: true
    }
});

Object.defineProperty(RError.prototype, 'stack', {
    get: function stack() {
        return this.name + ': ' + this.message + '\n' + this._err.stack.split('\n').slice(2).join('\n');
    }
});

Object.defineProperty(RError.prototype, 'why', {
    get: function why() {
        var _why = this.name + ': ' + this.message;
        for (var i = 1; i < this.chain.length; i++) {
            var e = this.chain[i];
            _why += ' <- ' + e.name + ': ' + e.message;
        }
        return _why;
    }
});

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}

версия es6:

class RError extends Error {
    constructor({name, message, cause}) {
        super();
        this.name = name;
        this.message = message;
        this.cause = cause;
    }
    [Symbol.iterator]() {
        let current = this;
        let done = false;
        const iterator = {
            next() {
                const val = current;
                if (done) {
                    return { value: val, done: true };
                }
                current = current.cause;
                if (!val.cause) {
                    done = true;
                }
                return { value: val, done: false };
            }
        };
        return iterator;
    }
    get why() {
        let _why = '';
        for (const e of this) {
            _why += `${_why.length ? ' <- ' : ''}${e.name}: ${e.message}`;
        }
        return _why;
    }
}

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}

Я поместил свое решение в модуль, вот оно: https://www.npmjs.com/package/rerror

борисдиакур
источник
4

Мне нравится это делать так:

  • Используйте имя, чтобы toString () выбрасывал"{code}: {message}"
  • Вернуть то же самое в super, чтобы оно было таким же в трассировке стека
  • Прикрепить код к error.codeкакому-либо лучше проверять / разбирать код в коде, чем проверять сообщение, которое вы можете захотеть локализовать, например
  • Прикрепить сообщение к error.messageкак альтернативуerror.toString()

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

// Just a code
try {
  throw new AppException('FORBIDDEN');
} catch(e) {
  console.error(e);
  console.error(e.toString());
  console.log(e.code === 'FORBIDDEN');
}

// A code 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());
  console.log(e.code === 'FORBIDDEN');
}

Доминик
источник
2

Мне просто нужно было реализовать что-то подобное, и я обнаружил, что стек был потерян в моей собственной реализации ошибки. Что мне нужно было сделать, так это создать фиктивную ошибку и извлечь из нее стек:

My.Error = function (message, innerException) {
    var err = new Error();
    this.stack = err.stack; // IMPORTANT!
    this.name = "Error";
    this.message = message;
    this.innerException = innerException;
}
My.Error.prototype = new Error();
My.Error.prototype.constructor = My.Error;
My.Error.prototype.toString = function (includeStackTrace) {
    var msg = this.message;
    var e = this.innerException;
    while (e) {
        msg += " The details are:\n" + e.message;
        e = e.innerException;
    }
    if (includeStackTrace) {
        msg += "\n\nStack Trace:\n\n" + this.stack;
    }
    return msg;
}
Жюль
источник
Это не устанавливает сообщение
BT
2

Я использовал шаблон конструктора для создания нового объекта ошибки. Я определил цепочку прототипов, например, Errorэкземпляр. См. Справку по конструктору ошибок MDN .

Вы можете проверить этот фрагмент по этой сути .

РЕАЛИЗАЦИЯ

// Creates user-defined exceptions
var CustomError = (function() {
  'use strict';

  //constructor
  function CustomError() {
    //enforces 'new' instance
    if (!(this instanceof CustomError)) {
      return new CustomError(arguments);
    }
    var error,
      //handles the arguments object when is passed by enforcing a 'new' instance
      args = Array.apply(null, typeof arguments[0] === 'object' ? arguments[0] : arguments),
      message = args.shift() || 'An exception has occurred';

    //builds the message with multiple arguments
    if (~message.indexOf('}')) {
      args.forEach(function(arg, i) {
        message = message.replace(RegExp('\\{' + i + '}', 'g'), arg);
      });
    }

    //gets the exception stack
    error = new Error(message);
    //access to CustomError.prototype.name
    error.name = this.name;

    //set the properties of the instance
    //in order to resemble an Error instance
    Object.defineProperties(this, {
      stack: {
        enumerable: false,
        get: function() { return error.stack; }
      },
      message: {
        enumerable: false,
        value: message
      }
    });
  }

  // Creates the prototype and prevents the direct reference to Error.prototype;
  // Not used new Error() here because an exception would be raised here,
  // but we need to raise the exception when CustomError instance is created.
  CustomError.prototype = Object.create(Error.prototype, {
    //fixes the link to the constructor (ES5)
    constructor: setDescriptor(CustomError),
    name: setDescriptor('JSU Error')
  });

  function setDescriptor(value) {
    return {
      configurable: false,
      enumerable: false,
      writable: false,
      value: value
    };
  }

  //returns the constructor
  return CustomError;
}());

ПРИМЕНЕНИЕ

Конструктор CustomError может получать множество аргументов для создания сообщения, например

var err1 = new CustomError("The url of file is required"),
    err2 = new CustomError("Invalid Date: {0}", +"date"),
    err3 = new CustomError("The length must be greater than {0}", 4),
    err4 = new CustomError("Properties .{0} and .{1} don't exist", "p1", "p2");

throw err4;

А вот так выглядит кастомная ошибка:

Пользовательская цепочка прототипов ошибок

Jherax
источник
Тот, кто проголосовал против, есть ли у вас аргументы или причина для того, чтобы проголосовать против? или просто не понимает намерения в коде.
jherax 01
Я только что заметил, что, должно быть, случайно нажал кнопку отрицательного голоса при просмотре этой страницы, не осознавая этого (вероятно, просматривая с моего телефона). Я заметил только сегодня, просматривая свою историю. Это определенно было сделано не намеренно, но я не могу отменить его, так как срок действия истек. Вы дали информативный ответ и определенно не заслужили его. Если вы внесете правку, я с радостью отменю голос против. Прости за это.
jschr
2

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

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());
  }
});
Рафаль Энден
источник
2
class NotImplementedError extends Error {
  constructor(message) {
    super(message);
    this.message = message;
  }
}
NotImplementedError.prototype.name = 'NotImplementedError';
module.exports = NotImplementedError;

а также

try {
  var e = new NotImplementedError("NotImplementedError message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

Это просто классовое представление этого ответа.

выход

NotImplementedError: NotImplementedError message
  ...stacktrace
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
грамча
источник
1

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

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

x = new NotImplementedError();

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

Pluckyglen
источник
2
Вы должны включить иерархию типов ошибок или значение объекта в JavaScript, потому что вы можете указать только один блок catch. В вашем решении (x instanceof NotImplementedError) имеет значение false, что в моем случае неприемлемо.
cdleary
1

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

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

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

// Later on...

throw new NotImplementedError();
Арам Кочарян
источник
Это отлично работает, за исключением того, что в стеке будет дополнительная строка для конструктора ошибок, что может быть проблемой.
SystemParadox
Также не проходит error instanceof Errorтест, что может быть полезно.
Лорен
0

За счет невозможности использования 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
}
Gima
источник
все, что вам нужно сделать, чтобы использовать instanceof, - это выбросить новую fixError вместо fixError
BT
@BT: Не с fixErrorфункцией выше. Добавление newпри вызове просто создаст объект, который будет выброшен.
TJ Crowder
О, я предполагаю, что имел в виду использование instanceof fixError - конечно, тогда «instanceof Error» не сработает .. я думаю, это хуже ..
BT
0

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

function myError(msg){ 
      var e = new Error(msg); 
      _this = this; 
      _this.__proto__.__proto__ = e;
}
Чанду
источник
0

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

  • err instanceof Error
  • err instanceof CustomErrorType
  • console.log () возвращается [CustomErrorType]при создании с сообщением
  • console.log () возвращается [CustomErrorType: message]при создании без сообщения
  • throw / stack предоставляет информацию в момент создания ошибки.
  • Оптимально работает в Node.JS и Chrome.
  • Пройдет проверки instanceof в 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
Нуклон
источник
0

Для меня работало следующее, взятое из официальной документации Mozilla Error .

function NotImplementedError(message) {
    var instance = new Error(message);
    instance.name = 'NotImplementedError';

    Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
    if (Error.captureStackTrace) {
        Error.captureStackTrace(instance, NotImplementedError);
    }
    return instance;
}

NotImplementedError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
    }
});
Жунио
источник
-1

Попробуйте новый объект-прототип для каждого экземпляра определенного пользователем типа ошибки. Это позволяет 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;
}

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

Август Клинг
источник
Не работает. Попробуйте: var a = new NotImplementedError('a'), b = new NotImplementedError('b');. Сейчас a instanceof NotImplementedError == falseиb instanceof NotImplementedError == true
jjrv 07
-1

Это самый быстрый способ сделать это:

    let thisVar = false

    if (thisVar === false) {
            throw new Error("thisVar is false. It should be true.")
    }
jlhs
источник
-3

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

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

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

Пауло Энмануэль
источник