Я хочу добавить некоторые вещи в мой код JS, и я хочу, чтобы они были экземпляром Error, но я также хочу, чтобы они были чем-то другим.
В Python, как правило, подкласс Exception.
Что нужно делать в JS?
источник
Я хочу добавить некоторые вещи в мой код JS, и я хочу, чтобы они были экземпляром Error, но я также хочу, чтобы они были чем-то другим.
В Python, как правило, подкласс Exception.
Что нужно делать в JS?
Единственное стандартное поле объекта Error имеет message
свойство. (См. Спецификацию языка MDN или EcmaScript, раздел 15.11). Все остальное зависит от платформы.
Среда Mosts устанавливает stack
свойство, но fileName
и lineNumber
практически бесполезна для использования при наследовании.
Итак, минималистичный подход это:
function MyError(message) {
this.name = 'MyError';
this.message = message;
this.stack = (new Error()).stack;
}
MyError.prototype = new Error; // <-- remove this if you do not
// want MyError to be instanceof Error
Вы можете прослушать стек, снять с него ненужные элементы и извлечь информацию, такую как fileName и lineNumber, но для этого требуется информация о платформе, на которой в данный момент работает JavaScript. В большинстве случаев это не нужно - и вы можете сделать это после смерти, если действительно хотите.
Safari является заметным исключением. Нет stack
свойства, но throw
наборы ключевых слов sourceURL
и line
свойства объекта, который бросается. Эти вещи гарантированно будут правильными.
Тестовые примеры, которые я использовал, можно найти здесь: JavaScript самостоятельно сделал Сравнение объектов ошибок .
this.name = 'MyError'
внешнюю часть функции и изменить ее наMyError.prototype.name = 'MyError'
.function MyError(message) { this.message = message; this.stack = Error().stack; } MyError.prototype = Object.create(Error.prototype); MyError.prototype.name = "MyError";
MyError.prototype.constructor = MyError
тоже добавил .this
, верно?В ES6:
источник
источник
var supportsClasses = false; try {eval('class X{}'); supportsClasses = true;} catch (e) {}
this.name = this.constructor.name;
вместо этого.Короче говоря:
Если вы используете ES6 без транспортеров :
Если вы используете транспортер Babel :
Вариант 1: использовать babel-plugin-transform-builtin-extend
Вариант 2: сделай сам (по мотивам той же библиотеки)
Если вы используете чистый ES5 :
Альтернатива: использовать Classtrophobic framework
Объяснение:
Почему расширение класса Error с помощью ES6 и Babel является проблемой?
Потому что экземпляр CustomError больше не распознается как таковой.
В самом деле, из официальной документации Вавилонского, вы не можете расширить любой встроенные классы JavaScript , такие как
Date
,Array
,DOM
илиError
.Проблема описана здесь:
А как насчет других ответов SO?
Все приведенные ответы решают
instanceof
проблему, но вы теряете обычную ошибкуconsole.log
:Принимая во внимание, что, используя метод, упомянутый выше, вы не только исправляете
instanceof
проблему, но также сохраняете обычную ошибкуconsole.log
:источник
class CustomError extends Error { /* ... */}
неправильно обрабатывает специфичные для поставщика аргументы (lineNumber
и т. д.), «Расширение ошибки в Javascript с синтаксисом ES6» является специфическим для Babel, ваше решение ES5 используетconst
и не обрабатывает пользовательские аргументы.console.log(new CustomError('test') instanceof CustomError);// false
была верной на момент написания, но теперь была решена. Фактически, проблема, связанная с ответом , была решена, и мы можем проверить правильное поведение здесь , вставив код в REPL и посмотрев, как он правильно передается для создания экземпляра с правильной цепочкой прототипов.Изменить: Пожалуйста, прочитайте комментарии. Оказывается, это хорошо работает только в V8 (Chrome / Node.JS). Моим намерением было создание кросс-браузерного решения, которое работало бы во всех браузерах и обеспечивало трассировку стека там, где есть поддержка.
Изменить: я сделал это вики сообщества, чтобы позволить больше редактирования.
Решение для V8 (Chrome / Node.JS), работает в Firefox и может быть изменено для корректной работы в IE. (см. конец поста)
Оригинальный пост "Покажи мне код!"
Укороченная версия:
Я держу
this.constructor.prototype.__proto__ = Error.prototype
внутри функции, чтобы держать весь код вместе. Но вы также можете заменитьthis.constructor
на,UserError
и это позволит вам переместить код за пределы функции, поэтому он вызывается только один раз.Если вы идете по этому маршруту, убедитесь, что вы позвонили по этой линии до того, как в первый раз бросите
UserError
.Это предостережение не применяет функцию, потому что функции создаются первыми, независимо от порядка. Таким образом, вы можете без проблем переместить функцию в конец файла.
Совместимость браузера
Работает в Firefox и Chrome (и Node.JS) и выполняет все обещания.
Internet Explorer не работает в следующем
Ошибки не должны
err.stack
начинаться с, поэтому "это не моя вина".Error.captureStackTrace(this, this.constructor)
не существует, поэтому вам нужно сделать что-то еще, какtoString
перестает существовать, когда вы подклассError
. Так что вам тоже нужно добавить.IE не будет считаться
UserError
,instanceof Error
если вы не выполните следующее некоторое время, прежде чемthrow UserError
источник
Error.call(this)
действительно ничего не делает, так как он возвращает ошибку, а не изменениеthis
.UserError.prototype = Error.prototype
вводит в заблуждение. Это не делает наследование, это делает их одним и тем же классом .Object.setPrototypeOf(this.constructor.prototype, Error.prototype)
что предпочтительнееthis.constructor.prototype.__proto__ = Error.prototype
, по крайней мере, для современных браузеров.Чтобы избежать шаблона для каждого типа ошибки, я объединил мудрость некоторых решений в
createErrorType
функцию:Затем вы можете легко определить новые типы ошибок следующим образом:
источник
this.name = name;
?name
он уже установлен на прототип, он больше не нужен. Я удалил это. Спасибо!В 2018 году я думаю, что это лучший способ; который поддерживает IE9 + и современные браузеры.
ОБНОВЛЕНИЕ : см. Этот тест и репо для сравнения на разных реализациях.
Также помните, что
__proto__
свойство устарело, что широко используется в других ответах.источник
setPrototypeOf()
? По крайней мере, согласно MDN, обычно не рекомендуется использовать его, если вы можете выполнить то же самое, просто установив.prototype
свойство в конструкторе (как вы делаете вelse
блоке для браузеров, которые не имеютsetPrototypeOf
).setPrototypeOf
. Но если вам все еще это нужно (как требует ОП), вам следует использовать встроенную методологию. Как указывает MDN, это считается правильным способом установки прототипа объекта. Другими словами, MDN говорит, что не изменяйте прототип (поскольку это влияет на производительность и оптимизацию), но, если вам нужно, используйтеsetPrototypeOf
.CustomError.prototype = Object.create(Error.prototype)
). Кроме того,Object.setPrototypeOf(CustomError, Error.prototype)
это установка прототипа самого конструктора, а не указание прототипа для новых экземпляровCustomError
. Во всяком случае, в 2016 году я думаю, что на самом деле есть лучший способ расширить ошибки, хотя я все еще выясняю, как использовать их вместе с Babel: github.com/loganfsmyth/babel-plugin-transform-builtin-extend/…CustomError.prototype = Object.create(Error.prototype)
Также меняется прототип. Вы должны изменить его, поскольку в ES5 нет встроенной логики расширения / наследования. Я уверен, что плагин Babel, который вы упоминаете, делает подобные вещи.Object.setPrototypeOf
здесь не имеет смысла, по крайней мере, не так, как вы его используете: gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348 . Возможно, вы намеревались написатьObject.setPrototypeOf(CustomError.prototype, Error.prototype)
- это имело бы немного больше смысла (хотя все равно не давало никакой выгоды по сравнению с простыми настройкамиCustomError.prototype
)Для полноты картины - просто потому, что ни один из предыдущих ответов не упомянул этот метод - если вы работаете с Node.js и вам не нужно заботиться о совместимости браузера, желаемый эффект довольно легко достичь с помощью встроенного
inherits
изutil
модуля ( официальные документы здесь ).Например, предположим, что вы хотите создать собственный класс ошибок, который принимает код ошибки в качестве первого аргумента и сообщение об ошибке в качестве второго аргумента:
файл custom-error.js :
Теперь вы можете создать экземпляр и передать / выбросить
CustomError
:Обратите внимание, что в этом фрагменте трассировка стека будет иметь правильное имя файла и строку, а экземпляр ошибки будет иметь правильное имя!
Это происходит из-за использования
captureStackTrace
метода, который создаетstack
свойство для целевого объекта (в данном случае, дляCustomError
экземпляра). Для получения более подробной информации о том, как это работает, проверьте документацию здесь .источник
this.message = this.message;
это неправильно или есть еще сумасшедшие вещи, которые я не знаю о JS?Ответ Crescent Fresh с высоким рейтингом вводит в заблуждение. Хотя его предупреждения недействительны, есть и другие ограничения, которые он не рассматривает.
Во-первых, рассуждения в параграфе «Предостережения» Полумесяца не имеют смысла. Объяснение подразумевает, что кодирование "связки if (error instanceof MyError) else ..." является несколько обременительным или многословным по сравнению с несколькими операторами catch. Несколько операторов instanceof в одном блоке catch так же кратки, как и несколько операторов catch - чистый и лаконичный код без каких-либо хитростей. Это отличный способ эмулировать отличную обработку ошибок, характерную для бросаемых подтипов Java.
WRT «появляется сообщение, что свойство подкласса не устанавливается», это не тот случай, если вы используете правильно сконструированный подкласс Error. Чтобы создать собственный подкласс ErrorX Error, просто скопируйте блок кода, начинающийся с «var MyError =», заменив одно слово «MyError» на «ErrorX». (Если вы хотите добавить пользовательские методы в свой подкласс, следуйте примеру текста).
Реальное и существенное ограничение подклассов ошибок JavaScript заключается в том, что для реализаций JavaScript или отладчиков, которые отслеживают и сообщают о трассировке стека и расположении экземпляров, таких как FireFox, местоположение в вашей собственной реализации подкласса ошибок будет записываться как точка создания экземпляров. класс, тогда как если бы вы использовали прямую ошибку, это будет место, где вы запустили «новая ошибка (...)»). Пользователи IE, вероятно, никогда этого не заметят, но пользователи Fire Bug в FF увидят бесполезные значения имен файлов и номеров строк, сообщаемые вместе с этими ошибками, и им придется детализировать трассировку стека до элемента # 1, чтобы найти реальное местоположение экземпляра.
источник
Crescent Fresh's
был удален!Как насчет этого решения?
Вместо того, чтобы выдавать свою собственную ошибку, используя:
Вы бы обернули объект Error (вроде декоратора):
Это гарантирует, что все атрибуты являются правильными, такие как стек, имя_файла lineNumber и т. Д.
Все, что вам нужно сделать, это либо скопировать атрибуты, либо определить для них геттеры. Вот пример использования геттеров (IE9):
источник
new MyErr (arg1, arg2, new Error())
и в конструкторе MyErr мы используемObject.assign
для присваивания свойств последнего аргументаthis
Как говорят некоторые люди, с ES6 это довольно просто:
Я попробовал это в своем приложении (Angular, Typescript), и оно просто не сработало. Через некоторое время я обнаружил, что проблема исходит от Typescript: O
См. Https://github.com/Microsoft/TypeScript/issues/13965.
Это очень беспокоит, потому что если вы делаете:
В узле или непосредственно в вашем браузере будет отображаться:
Custom error
Попробуйте запустить это с Typescript в вашем проекте на площадке Typescript, он будет отображаться
Basic error
...Решение состоит в том, чтобы сделать следующее:
источник
Мое решение более простое, чем другие ответы, и не имеет недостатков.
Он сохраняет цепочку прототипов Error и все свойства Error, не требуя специальных знаний о них. Он был протестирован в Chrome, Firefox, Node и IE11.
Единственным ограничением является дополнительная запись в верхней части стека вызовов. Но это легко игнорируется.
Вот пример с двумя пользовательскими параметрами:
Пример использования:
Для сред, которым требуется полифайл setPrototypeOf:
источник
throw CustomError('err')
вместоthrow new CustomError('err')
В приведенном выше примере
Error.apply
(такжеError.call
) ничего не делает для меня (Firefox 3.6 / Chrome 5). Обходной путь, который я использую:источник
В Node, как говорили другие, все просто:
источник
Я просто хочу добавить к тому, что уже заявили другие:
Чтобы убедиться, что пользовательский класс ошибок правильно отображается в трассировке стека, необходимо установить для свойства имени прототипа пользовательского класса ошибок значение свойства имени пользовательского класса ошибок. Это то, что я имею в виду:
Таким образом, полный пример будет:
Когда все сказано и сделано, вы бросаете свое новое исключение, и это выглядит так (я лениво пробовал это в инструментах Chrome Dev):
источник
Мои 2 цента:
Почему другой ответ?
а) Потому что доступ к
Error.stack
свойству (как в некоторых ответах) имеет большое снижение производительности.б) Потому что это только одна строка.
c) Потому что решение по адресу https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error , похоже, не сохраняет информацию стека.
пример использования
http://jsfiddle.net/luciotato/xXyeB/
Что оно делает?
this.__proto__.__proto__
естьMyError.prototype.__proto__
, поэтому он устанавливает__proto__
для ВСЕХ ВСТРОЕНИЙ MyError конкретную вновь созданную ошибку. Он сохраняет свойства и методы класса MyError, а также помещает новые свойства Error (включая .stack) в__proto__
цепочку.Очевидная проблема:
Вы не можете иметь более одного экземпляра MyError с полезной информацией стека.
Не используйте это решение, если вы не полностью понимаете, что
this.__proto__.__proto__=
делает.источник
Поскольку исключения JavaScript трудно подклассифицировать, я не подклассифицирую. Я просто создаю новый класс исключения и использую внутри него ошибку. Я изменяю свойство Error.name, чтобы оно выглядело как мое пользовательское исключение на консоли:
Вышеуказанное новое исключение может быть сгенерировано так же, как обычная ошибка, и оно будет работать, как и ожидалось, например:
Предостережение: трассировка стека не идеальна, так как она приведет вас туда, где создана новая Ошибка, а не туда, куда вы выбросили. Это не имеет большого значения для Chrome, потому что он обеспечивает полную трассировку стека прямо в консоли. Но это более проблематично, например, в Firefox.
источник
m = new InvalidInputError(); dontThrowMeYet(m);
m = new ...
тогдаPromise.reject(m)
. В этом нет необходимости, но код легче читать.Как указано в ответе Мохсена, в ES6 можно расширять ошибки, используя классы. Это намного проще, и их поведение в большей степени согласуется с собственными ошибками ... но, к сожалению, не так просто использовать это в браузере, если вам нужна поддержка браузеров до ES6. Ниже приведены некоторые заметки о том, как это может быть реализовано, но пока я предлагаю относительно простой подход, который включает некоторые из лучших предложений из других ответов:
В ES6 это так просто, как:
... и вы можете обнаружить поддержку классов ES6 с помощью
try {eval('class X{}')
, но вы получите синтаксическую ошибку, если попытаетесь включить версию ES6 в скрипт, загружаемый старыми браузерами. Поэтому единственным способом поддержки всех браузеров будет динамическая загрузка отдельного скрипта (например, через AJAX илиeval()
) для браузеров, которые поддерживают ES6. Еще одним осложнением является то, чтоeval()
он поддерживается не во всех средах (из-за политик безопасности содержимого), что может или не может быть соображением для вашего проекта.Так что на данный момент первый подход, описанный выше, или просто
Error
непосредственное использование без попыток его расширения, кажется лучшим из того, что практически можно сделать для кода, который должен поддерживать браузеры не-ES6.Есть еще один подход, который некоторые люди, возможно, захотят рассмотреть, который заключается в использовании,
Object.setPrototypeOf()
где это возможно, для создания объекта ошибки, который является экземпляром вашего пользовательского типа ошибки, но который выглядит и ведет себя больше как собственная ошибка в консоли (благодаря ответу Бена за рекомендацию). Вот мой взгляд на этот подход: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 . Но, учитывая, что однажды мы сможем просто использовать ES6, лично я не уверен, что сложность такого подхода того стоит.источник
Способ сделать это правильно - вернуть результат применения из конструктора, а также установить прототип обычным сложным способом javascripty:
Единственные проблемы с этим способом сделать это в данный момент (я повторил это немного), что
stack
иmessage
не включены вMyError
иПервая проблема может быть решена путем итерации всех неперечислимых свойств ошибки с помощью хитрости в этом ответе: возможно ли получить неперечислимые имена унаследованных свойств объекта? , но это не поддерживается то есть <9. Вторую проблему можно решить, вырвав эту строку из трассировки стека, но я не уверен, как это безопасно сделать (возможно, просто удалив вторую строку из e.stack.toString () ??).
источник
Фрагмент показывает все это.
источник
Я бы сделал шаг назад и подумал, почему ты хочешь это сделать? Я думаю, что дело в разных ошибках по-разному.
Например, в Python вы можете ограничить оператор catch только catch
MyValidationError
, и, возможно, вы захотите сделать что-то подобное в javascript.Вы не можете сделать это в JavaScript. Там будет только один блок поймать. Вы должны использовать оператор if для определения ошибки.
catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }
Я думаю, что вместо этого бросил бы необработанный объект с типом, сообщением и любыми другими свойствами, которые вы считаете подходящими.
И когда вы ловите ошибку:
источник
error.stack
, стандартный инструмент не будет работать с ним и т. Д. И т. Д. Лучше было бы добавить свойства к экземпляру ошибки, например,var e = new Error(); e.type = "validation"; ...
Декоратор пользовательских ошибок
Это основано на ответе Джорджа Бэйли , но расширяет и упрощает первоначальную идею. Он написан на CoffeeScript, но его легко конвертировать в JavaScript. Идея заключается в расширении пользовательской ошибки Bailey с помощью декоратора, который оборачивает ее, позволяя вам легко создавать новые пользовательские ошибки.
Примечание: это будет работать только в V8. Нет поддержки для
Error.captureStackTrace
других сред.определять
Декоратор принимает имя для типа ошибки и возвращает функцию, которая принимает сообщение об ошибке и включает имя ошибки.
использование
Теперь просто создавать новые типы ошибок.
Для забавы вы можете теперь определить функцию, которая выдает a,
SignatureError
если она вызывается со слишком большим количеством аргументов.Это было проверено довольно хорошо и, кажется, отлично работает на V8, поддерживая отслеживание, положение и т. Д.
Примечание. Использование
new
необязательно при создании пользовательской ошибки.источник
если вы не заботитесь о производительности из-за ошибок, это самое маленькое, что вы можете сделать
Вы можете использовать его без новых просто MyError (сообщение)
Изменяя прототип после вызова конструктора Error, нам не нужно устанавливать callstack и сообщение
источник
У Мохсена есть отличный ответ выше в ES6, который задает имя, но если вы используете TypeScript или живете в будущем, где, как мы надеемся, это предложение для полей открытого и закрытого классов перешло стадию 3 как предложение и сделало его на этапе 4 как часть ECMAScript / JavaScript, возможно, вы захотите узнать, что это будет немного короче. На этапе 3 браузеры начинают реализовывать функции, поэтому, если ваш браузер поддерживает их, приведенный ниже код может сработать. (Протестировано в новом браузере Edge v81, похоже, работает нормально). Имейте в виду, что на данный момент это нестабильная функция, которую следует использовать с осторожностью, и вы всегда должны проверять поддержку браузером нестабильных функций. Этот пост предназначен в основном для тех будущих жителей, когда браузеры могут это поддерживать. Чтобы проверить поддержку проверить MDNи могу ли я использовать . В настоящее время он пользуется поддержкой 66% на рынке браузеров, что не так уж и здорово, поэтому, если вы действительно хотите использовать его сейчас и не хотите ждать, либо используйте транспортер, такой как Babel, или что-то вроде TypeScript .
Сравните это с безымянной ошибкой, которая, если ее выбросить, не запишет ее имя.
источник