Почему-то похоже, что делегирование конструктора не работает в следующем фрагменте:
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
конструктором, о которой я не знаю?
javascript
exception
cdleary
источник
источник
Ответы:
Обновите свой код, чтобы присвоить прототипу 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"
и быть в порядке.источник
Error.prototype
напрямую - это, вероятно, плохой тон. Если позже вы захотите добавитьNotImplementedError.prototype.toString
к объекту псевдонимыError.prototype.toString
- лучше сделатьNotImplementedError.prototype = new Error()
.subclass.prototype = new Error()
это дурной тон . Вы должны использоватьsubclass.prototype = Object.create(superclass.prototype)
вместо этого. Я надеюсь, что это также может решить проблему трассировки стека.this.stack = new Error().stack;
NotImplementedError.prototype = Error.prototype;
не делаетinstanceof
лакомство вNotImplementedError
качестве подкласса изError
, этоinstanceof
рассматривать их как точно такой же класс. Если вы вставите приведенный выше код в свою консоль и попытаетесьnew Error() instanceof NotImplementedError
получитьtrue
, что явно неверно.Все вышеперечисленные ответы ужасно ужасны - правда. Даже тот, у которого 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источник
return this
в конструкторе.temp.name = this.name = 'MyError'
вы можете сделатьtemp.name = this.name = this.constructor.name
. Таким образом, это будет работать и для подклассовMyError
.В ES2015 вы можете использовать
class
для этого:class NotImplemented extends Error { constructor(message = "", ...args) { super(message, ...args); this.message = message + " has not yet been implemented."; } }
Это не изменяет глобальный
Error
прототип, позволяет настраиватьmessage
,name
и другие атрибуты, а также должным образом фиксирует стек. Кроме того, это довольно удобно для чтения.Конечно, вам может понадобиться инструмент, например,
babel
если ваш код будет работать в старых браузерах.источник
Если кому-то интересно, как создать собственную ошибку и получить трассировку стека:
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); }
источник
Этот раздел стандарта может объяснить, почему
Error.apply
вызов не инициализирует объект:В этом случае
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 = ''
источник
this
inError.apply(this, arguments);
? Я говорю, что вызов Error здесь создает новый объект, который выбрасывается; не инициализирует уже созданный объект, которому назначенnie
.NotImplementedError
реализация не должна возвращатьreturned
переменную?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;
источник
У меня была аналогичная проблема. Моя ошибка должна быть
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
свойство нестандартное, оно поддерживается в большинстве новых браузеров, что в моем случае приемлемо.источник
Согласно Joyent, вам не следует связываться со свойством стека (которое я вижу во многих приведенных здесь ответах), потому что это отрицательно скажется на производительности. Вот что они говорят:
Я хотел бы упомянуть их идею обертывания исходной ошибки, которая является хорошей заменой для передачи в стек.
Итак, вот как я создаю настраиваемую ошибку, учитывая вышесказанное:
версия 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
источник
Мне нравится это делать так:
"{code}: {message}"
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'); }
источник
Мне просто нужно было реализовать что-то подобное, и я обнаружил, что стек был потерян в моей собственной реализации ошибки. Что мне нужно было сделать, так это создать фиктивную ошибку и извлечь из нее стек:
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; }
источник
Я использовал шаблон конструктора для создания нового объекта ошибки. Я определил цепочку прототипов, например,
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;
А вот так выглядит кастомная ошибка:
источник
Это моя реализация:
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()); } });
источник
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
источник
Конструктор должен быть похож на фабричный метод и возвращать то, что вы хотите. Если вам нужны дополнительные методы / свойства, вы можете добавить их к объекту перед его возвратом.
function NotImplementedError(message) { return new Error("Not implemented", message); } x = new NotImplementedError();
Хотя я не уверен, зачем вам это нужно. Почему бы просто не использовать
new Error...
? Пользовательские исключения на самом деле мало что добавляют в JavaScript (или, возможно, любой нетипизированный язык).источник
Это прекрасно реализовано в Cesium DeveloperError:
В упрощенном виде:
var NotImplementedError = function(message) { this.name = 'NotImplementedError'; this.message = message; this.stack = (new Error()).stack; } // Later on... throw new NotImplementedError();
источник
error instanceof Error
тест, что может быть полезно.За счет невозможности использования
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 }
источник
fixError
функцией выше. Добавлениеnew
при вызове просто создаст объект, который будет выброшен.Другая альтернатива может работать не во всех средах. По крайней мере, уверена, что она работает в nodejs 0.8. В этом подходе используется нестандартный способ изменения внутренней прото-опоры.
function myError(msg){ var e = new Error(msg); _this = this; _this.__proto__.__proto__ = e; }
источник
Если вы используете Node / Chrome. Следующий фрагмент кода предоставит вам расширение, отвечающее следующим требованиям.
err instanceof Error
err instanceof CustomErrorType
[CustomErrorType]
при создании с сообщением[CustomErrorType: message]
при создании без сообщения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
источник
Для меня работало следующее, взятое из официальной документации 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 } });
источник
Попробуйте новый объект-прототип для каждого экземпляра определенного пользователем типа ошибки. Это позволяет
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
Это самый быстрый способ сделать это:
let thisVar = false if (thisVar === false) { throw new Error("thisVar is false. It should be true.") }
источник
более простой способ. Вы можете сделать свой объект унаследованным от объекта Error. Пример:
function NotImplementError(message) { this.message = message; Error.call(); Error.call(message); }
то, что мы делаем, - это использование функции call (), которая вызывает конструктор класса Error, поэтому в основном это то же самое, что и реализация наследования класса в других объектно-ориентированных языках.
источник
У MDN есть отличный пример :
try { throw new Error('Whoops!'); } catch (e) { console.log(e.name + ': ' + e.message); }
источник