JS koduma bazı şeyler atmak istiyorum ve ben Hata örneği olmasını istiyorum, ama aynı zamanda başka bir şey olmasını istiyorum.
Python'da, tipik olarak, istisna alt sınıf olur.
JS'de yapılacak uygun şey nedir?
JS koduma bazı şeyler atmak istiyorum ve ben Hata örneği olmasını istiyorum, ama aynı zamanda başka bir şey olmasını istiyorum.
Python'da, tipik olarak, istisna alt sınıf olur.
JS'de yapılacak uygun şey nedir?
Yanıtlar:
Error nesnesinin sahip olduğu tek standart alan message
özelliğidir. (Bkz. MDN veya EcmaScript Dil Spesifikasyonu, bölüm 15.11) Diğer her şey platforma özgüdür.
Mosts ortamları set stack
özelliği, ancak fileName
ve lineNumber
miras kullanılmak üzere pratik olarak işe yaramaz.
Yani minimalist yaklaşım:
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
Yığını koklayabilir, istenmeyen öğeleri kaldırabilir ve fileName ve lineNumber gibi bilgileri çıkarabilirsiniz, ancak bunu yapmak için JavaScript'in şu anda çalıştığı platform hakkında bilgi gerekir. Çoğu durumda gereksizdir - ve eğer isterseniz gerçekten ölüm sonrası yapabilirsiniz.
Safari dikkate değer bir istisnadır. stack
Özellik yok , ancak throw
anahtar kelime kümeleri sourceURL
ve line
atılan nesnenin özellikleri. Bu şeylerin doğru olduğu garanti edilmektedir.
Kullandığım test senaryolarını burada bulabilirsiniz: JavaScript kendi kendine yapılan Hata nesnesi karşılaştırması .
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
, değil mi?
ES6'da:
class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}
var supportsClasses = false; try {eval('class X{}'); supportsClasses = true;} catch (e) {}
this.name = this.constructor.name;
bunun yerine kullanın.
Kısacası:
Transferi olmayan ES6 kullanıyorsanız :
class CustomError extends Error { /* ... */}
Babel transpiler kullanıyorsanız :
Seçenek 1: Babel-Plugin-Transform-Builtin-Extend kullanın
2.Seçenek: Kendiniz yapın (aynı kütüphaneden esinlenerek)
function CustomError(...args) {
const instance = Reflect.construct(Error, args);
Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
return instance;
}
CustomError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
});
Reflect.setPrototypeOf(CustomError, Error);
Saf ES5 kullanıyorsanız :
function CustomError(message, fileName, lineNumber) {
var instance = new Error(message, fileName, lineNumber);
Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
return instance;
}
CustomError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
});
if (Object.setPrototypeOf){
Object.setPrototypeOf(CustomError, Error);
} else {
CustomError.__proto__ = Error;
}
Alternatif: Classtrophobic framework kullanın
Açıklama:
Error sınıfını ES6 ve Babel kullanarak genişletmek neden bir sorundur?
Çünkü CustomError örneği artık bu şekilde tanınmıyor.
class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false
Aslında, Babil resmi belgelerinden, sen herhangi yerleşik JavaScript sınıfları uzatamaz gibi Date
, Array
, DOM
veya Error
.
Sorun burada açıklanmaktadır:
Diğer SO cevapları ne olacak?
Verilen tüm yanıtlar sorunu giderir, instanceof
ancak normal hatayı kaybedersiniz console.log
:
console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
Yukarıda belirtilen yöntemi kullanırken, yalnızca instanceof
sorunu düzeltmekle kalmaz, aynı zamanda normal hatayı da tutarsınız console.log
:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
class CustomError extends Error { /* ... */}
satıcıya özgü bağımsız değişkenleri ( lineNumber
vb.) doğru şekilde işlemezse, 'Javascript'te ES6 sözdizimi ile Uzatma Hatası' Babil'e özgüdür, ES5 çözümünüz kullanır const
ve özel bağımsız değişkenleri işlemez.
console.log(new CustomError('test') instanceof CustomError);// false
yazma sırasında doğru olduğunu, ancak şimdi çözüldüğünü belirtmek gerekir . Aslında cevap bağlantılı sorunu çözüldüğünü ve biz doğru davranışı test edebilirsiniz burada ve kod yapıştırarak REPL ve doğru doğru prototip zinciri ile örneğini transpiled alır nasıl gördükten.
Düzenle: Lütfen yorumları okuyun. Bu sadece V8 (Chrome / Node.JS) 'de iyi çalışıyor. Amacım tüm tarayıcılarda çalışan bir çapraz tarayıcı çözümü sağlamak ve desteğin olduğu yerde yığın izlemesi sağlamaktı.
Düzenleme: Bu Topluluk Wiki'sini daha fazla düzenlemeye izin verdim.
V8 (Chrome / Node.JS) çözümü, Firefox'ta çalışır ve çoğunlukla IE'de çalışacak şekilde değiştirilebilir. (yazının sonuna bakın)
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.
Error.call(this) // Does not seem necessary. Perhaps remove this line?
Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter
this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"
this.message = message; // Used to set the message
}
Kısa versiyon:
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
Ben tutmak this.constructor.prototype.__proto__ = Error.prototype
hep birlikte kodunu tutmak için işlev içinde. Ancak this.constructor
bununla da değiştirebilirsiniz UserError
ve bu da kodu fonksiyonun dışına taşımanıza izin verir, böylece sadece bir kez çağrılır.
O rotaya giderseniz , ilk defa atmadan önce bu hattı aradığınızdan emin olun UserError
.
Bu uyarı işlevi uygulamaz, çünkü sipariş ne olursa olsun önce işlevler oluşturulur. Böylece, işlevi sorunsuz bir şekilde dosyanın sonuna taşıyabilirsiniz.
Tarayıcı Uyumluluğu
Firefox ve Chrome'da (ve Node.JS) çalışır ve tüm vaatleri yerine getirir.
Internet Explorer aşağıda başarısız oluyor
Hataların err.stack
başlaması gerekmez , bu yüzden "bu benim hatam değil".
Error.captureStackTrace(this, this.constructor)
mevcut değil, bu yüzden böyle bir şey yapmalısın
if(Error.captureStackTrace) // AKA if not IE
Error.captureStackTrace(this, this.constructor)
toString
alt sınıfta kalmayı bırakır Error
. Bu yüzden de eklemeniz gerekiyor.
else
this.toString = function () { return this.name + ': ' + this.message }
Eğer UserError
bir instanceof Error
süre önce aşağıdaki çalıştırmak sürece IE bir olarak kabul etmeyecektirthrow UserError
UserError.prototype = Error.prototype
Error.call(this)
değiştirmekten ziyade hata döndürdüğü için hiçbir şey yapmıyor this
.
UserError.prototype = Error.prototype
yanıltıcıdır. Bu miras yapmaz, bu onları aynı sınıf yapar .
Object.setPrototypeOf(this.constructor.prototype, Error.prototype)
tercih edildiğine inanıyorum this.constructor.prototype.__proto__ = Error.prototype
.
To klişe önlemek hata her farklı türü için, bir içine çözümlerin bazılarının bilgelik kombine createErrorType
fonksiyonu:
function createErrorType(name, init) {
function E(message) {
if (!Error.captureStackTrace)
this.stack = (new Error()).stack;
else
Error.captureStackTrace(this, this.constructor);
this.message = message;
init && init.apply(this, arguments);
}
E.prototype = new Error();
E.prototype.name = name;
E.prototype.constructor = E;
return E;
}
Ardından yeni hata türlerini aşağıdaki gibi kolayca tanımlayabilirsiniz :
var NameError = createErrorType('NameError', function (name, invalidChar) {
this.message = 'The name ' + name + ' may not contain ' + invalidChar;
});
var UnboundError = createErrorType('UnboundError', function (variableName) {
this.message = 'Variable ' + variableName + ' is not bound';
});
this.name = name;
mı?
name
Prototip üzerinde zaten ayarlandığından, artık gerekli değil. Kaldırdım. Teşekkürler!
In 2018 , ben bu en iyi yol olduğunu düşünüyorum; IE9 + ve modern tarayıcıları destekler.
GÜNCELLEME : Farklı uygulamalarla karşılaştırma için bu test ve repo'ya bakın .
function CustomError(message) {
Object.defineProperty(this, 'name', {
enumerable: false,
writable: false,
value: 'CustomError'
});
Object.defineProperty(this, 'message', {
enumerable: false,
writable: true,
value: message
});
if (Error.hasOwnProperty('captureStackTrace')) { // V8
Error.captureStackTrace(this, CustomError);
} else {
Object.defineProperty(this, 'stack', {
enumerable: false,
writable: false,
value: (new Error(message)).stack
});
}
}
if (typeof Object.setPrototypeOf === 'function') {
Object.setPrototypeOf(CustomError.prototype, Error.prototype);
} else {
CustomError.prototype = Object.create(Error.prototype, {
constructor: { value: CustomError }
});
}
Ayrıca , diğer yanıtlarda yaygın olarak kullanılan __proto__
mülkün kullanımdan kaldırıldığına dikkat edin .
setPrototypeOf()
? En azından MDN'ye göre, genellikle .prototype
yapıcıdaki özelliği ayarlayarak aynı şeyi başarabilirseniz, kullanmak istemezsiniz ( else
olmayan taramalar için blokta yaptığınız gibi setPrototypeOf
).
setPrototypeOf
. Ancak yine de ihtiyacınız varsa (OP'nin istediği gibi), yerleşik yöntemi kullanmalısınız. MDN'nin belirttiği gibi, bu, bir nesnenin prototipini ayarlamak için uygun bir yol olarak kabul edilir. Başka bir deyişle MDN, prototipi değiştirmediğini (performansı ve optimizasyonu etkilediğinden) değil, kullanmanız gerektiğini söylüyor setPrototypeOf
.
CustomError.prototype = Object.create(Error.prototype)
) kullanabilirsiniz. Ayrıca, Object.setPrototypeOf(CustomError, Error.prototype)
yeni örneklerin prototipini belirtmek yerine yapıcısının prototipini ayarlamaktır CustomError
. Her neyse, 2016'da bence hataları genişletmenin daha iyi bir yolu var, ancak yine de Babel ile birlikte nasıl kullanılacağını anlıyorum: github.com/loganfsmyth/babel-plugin-transform-builtin-extend/…
CustomError.prototype = Object.create(Error.prototype)
prototipi de değiştiriyor. ES5'te yerleşik bir genişletme / devralma mantığı olmadığından bunu değiştirmeniz gerekir. Eminim bahsettiğiniz babel eklentisi benzer şeyler yapar.
Object.setPrototypeOf
En azından nasıl kullandığınızı değil, burada kullanımın neden mantıklı olmadığını gösteren bir gist oluşturdum : gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348 . Belki de yazmak istediniz Object.setPrototypeOf(CustomError.prototype, Error.prototype)
- bu biraz daha mantıklı olacaktır (yine de basitçe ayarlamaya hiçbir fayda sağlamaz CustomError.prototype
).
Tamlık uğruna - sadece önceki cevapların hiçbiri bu yöntemden bahsetmediği için - Node.js ile çalışıyorsanız ve tarayıcı uyumluluğunu önemsemek zorunda değilseniz, yerleşik etki ile istenen etkiyi elde etmek oldukça kolaydır. inherits
ait util
modülü ( resmi dokümanlar burada ).
Örneğin, birinci bağımsız değişken olarak bir hata kodu ve ikinci bağımsız değişken olarak hata iletisini alan özel bir hata sınıfı oluşturmak istediğinizi varsayalım:
custom-error.js dosyası :
'use strict';
var util = require('util');
function CustomError(code, message) {
Error.captureStackTrace(this, CustomError);
this.name = CustomError.name;
this.code = code;
this.message = message;
}
util.inherits(CustomError, Error);
module.exports = CustomError;
Şimdi aşağıdakileri başlatabilir ve geçebilir / atabilirsiniz CustomError
:
var CustomError = require('./path/to/custom-error');
// pass as the first argument to your callback
callback(new CustomError(404, 'Not found!'));
// or, if you are working with try/catch, throw it
throw new CustomError(500, 'Server Error!');
Bu snippet ile yığın izlemesinde doğru dosya adı ve satırı ve hata örneğinin doğru adı olacağını unutmayın!
Bu , hedef nesne üzerinde captureStackTrace
bir stack
özellik oluşturan yöntemin kullanılması nedeniyle oluşur (bu durumda, CustomError
örnekleme). Nasıl çalıştığı hakkında daha fazla ayrıntı için belgelerini kontrol buraya .
this.message = this.message;
bu yanlış mı yoksa JS hakkında bilmediğim hala çılgınca şeyler var mı?
Crescent Fresh'in cevabı yüksek oy alan cevabı yanıltıcıdır. Uyarıları geçersiz olsa da, ele almadığı başka sınırlamalar da var.
Birincisi, Crescent'in "Uyarılar:" paragrafındaki mantık mantıklı değil. Açıklama "bir sürü if (MyError örneğinde hata) ..." kodlamanın birden fazla catch deyimiyle karşılaştırıldığında bir şekilde külfetli veya ayrıntılı olduğunu ima eder. Tek bir catch bloğundaki çoklu instanceof deyimleri, birden fazla catch deyimi kadar özlüdür - hile olmadan temiz ve özlü kod. Bu, Java'nın tek kullanımlık alt tipine özgü hata işlemeyi taklit etmenin harika bir yoludur.
WRT "alt sınıfın message özelliği ayarlanmadıysa görünür", düzgün yapılandırılmış bir Hata alt sınıfı kullanırsanız durum böyle değildir. Kendi ErrorX Error alt sınıfınızı yapmak için, "var MyError =" ile başlayan ve "MyError" kelimesini "ErrorX" olarak değiştirerek kod bloğunu kopyalamanız yeterlidir. (Alt sınıfınıza özel yöntemler eklemek istiyorsanız, örnek metni izleyin).
JavaScript hata alt sınıflamasının gerçek ve önemli sınırlaması, FireFox gibi yığın izlemesi ve örnekleme konumu hakkında izleyen ve raporlayan JavaScript uygulamaları veya hata ayıklayıcıları için, kendi Hata alt sınıf uygulamanızdaki bir konumun doğrudan bir hata kullandıysanız "new Error (...)") çalıştırdığınız konum olacaktır. IE kullanıcıları muhtemelen hiçbir zaman fark etmeyeceklerdi, ancak FF'deki Fire Bug kullanıcıları, bu Hataların yanı sıra rapor edilen işe yaramaz dosya adı ve satır numarası değerlerini görecek ve gerçek örnekleme konumunu bulmak için yığın izini # 1'e indirmeleri gerekecek.
Crescent Fresh's
silindi gibi kafa karıştırıcı bazı !
Bu çözüme ne dersiniz?
Özel Hatanızı kullanarak:
throw new MyError("Oops!");
Error nesnesini sararsınız (bir dekoratör gibi):
throw new MyError(Error("Oops!"));
Bu, stack, fileName lineNumber, et cetera gibi tüm özniteliklerin doğru olmasını sağlar.
Daha sonra tek yapmanız gereken, ya niteliklerin üzerine kopyalamak ya da onlar için alıcıları tanımlamaktır. İşte getters (IE9) kullanan bir örnek:
function MyError(wrapped)
{
this.wrapped = wrapped;
this.wrapped.name = 'MyError';
}
function wrap(attr)
{
Object.defineProperty(MyError.prototype, attr, {
get: function()
{
return this.wrapped[attr];
}
});
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;
wrap('name');
wrap('message');
wrap('stack');
wrap('fileName');
wrap('lineNumber');
wrap('columnNumber');
MyError.prototype.toString = function()
{
return this.wrapped.toString();
};
new MyErr (arg1, arg2, new Error())
ve MyErr yapıcısında Object.assign
son this
Bazı insanların söylediği gibi, ES6 ile oldukça kolay:
class CustomError extends Error { }
Bu yüzden benim app, (Açısal, Typescript) içinde denedim ve işe yaramadı. Bir süre sonra sorunun Typescript'ten geldiğini gördüm: O
Bkz. Https://github.com/Microsoft/TypeScript/issues/13965
Çok rahatsız edici çünkü eğer yaparsan:
class CustomError extends Error {}
try {
throw new CustomError()
} catch(e) {
if (e instanceof CustomError) {
console.log('Custom error');
} else {
console.log('Basic error');
}
}
Düğümde veya doğrudan tarayıcınızda şunları görüntüler: Custom error
Dizginiz oyun alanında projenizde Typescript ile çalıştırmayı deneyin, görüntülenecektir Basic error
...
Çözüm aşağıdakileri yapmaktır:
class CustomError extends Error {
// we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965
// otherwise we cannot use instanceof later to catch a given type
public __proto__: Error;
constructor(message?: string) {
const trueProto = new.target.prototype;
super(message);
this.__proto__ = trueProto;
}
}
Benim çözümüm sağlanan diğer cevaplardan daha basit ve dezavantajları yok.
Hata prototip zincirini ve Hata üzerindeki tüm özelliklerini, özel bir bilgiye ihtiyaç duymadan korur. Chrome, Firefox, Node ve IE11'de test edildi.
Tek sınırlama, çağrı yığınının üst kısmındaki fazladan bir giriştir. Ancak bu kolayca göz ardı edilir.
İki özel parametreye sahip bir örnek:
function CustomError(message, param1, param2) {
var err = new Error(message);
Object.setPrototypeOf(err, CustomError.prototype);
err.param1 = param1;
err.param2 = param2;
return err;
}
CustomError.prototype = Object.create(
Error.prototype,
{name: {value: 'CustomError', enumerable: false}}
);
Örnek Kullanım:
try {
throw new CustomError('Something Unexpected Happened!', 1234, 'neat');
} catch (ex) {
console.log(ex.name); //CustomError
console.log(ex.message); //Something Unexpected Happened!
console.log(ex.param1); //1234
console.log(ex.param2); //neat
console.log(ex.stack); //stacktrace
console.log(ex instanceof Error); //true
console.log(ex instanceof CustomError); //true
}
SetPrototypeOf bir polifil gerektiren ortamlar için:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
};
throw CustomError('err')
throw new CustomError('err')
Yukarıdaki örnekte Error.apply
(ayrıca Error.call
) benim için hiçbir şey yapılmaz (Firefox 3.6 / Chrome 5). Kullandığım bir geçici çözüm:
function MyError(message, fileName, lineNumber) {
var err = new Error();
if (err.stack) {
// remove one stack level:
if (typeof(Components) != 'undefined') {
// Mozilla:
this.stack = err.stack.substring(err.stack.indexOf('\n')+1);
}
else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {
// Google Chrome/Node.js:
this.stack = err.stack.replace(/\n[^\n]*/,'');
}
else {
this.stack = err.stack;
}
}
this.message = message === undefined ? err.message : message;
this.fileName = fileName === undefined ? err.fileName : fileName;
this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
MyError.prototype.name = 'MyError';
Düğümde diğerlerinin söylediği gibi, basit:
class DumbError extends Error {
constructor(foo = 'bar', ...params) {
super(...params);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, DumbError);
}
this.name = 'DumbError';
this.foo = foo;
this.date = new Date();
}
}
try {
let x = 3;
if (x < 10) {
throw new DumbError();
}
} catch (error) {
console.log(error);
}
Sadece başkalarının zaten söylediklerine eklemek istiyorum:
Özel hata sınıfının yığın izlemesinde düzgün göründüğünden emin olmak için, özel hata sınıfının prototipinin name özelliğini özel hata sınıfının name özelliğine ayarlamanız gerekir. Demek istediğim bu:
CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';
Yani tam örnek:
var CustomError = function(message) {
var err = new Error(message);
err.name = 'CustomError';
this.name = err.name;
this.message = err.message;
//check if there is a stack property supported in browser
if (err.stack) {
this.stack = err.stack;
}
//we should define how our toString function works as this will be used internally
//by the browser's stack trace generation function
this.toString = function() {
return this.name + ': ' + this.message;
};
};
CustomError.prototype = new Error();
CustomError.prototype.name = 'CustomError';
Her şey söylendiğinde ve yapıldığında, yeni istisnınızı atarsınız ve şöyle görünür (Bunu krom dev araçlarında tembel olarak denedim):
CustomError: Stuff Happened. GASP!
at Error.CustomError (<anonymous>:3:19)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:603:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)
at Object.InjectedScript.evaluate (<anonymous>:481:21)
2 sentim:
a) Tesise erişimin Error.stack
(bazı cevaplarda olduğu gibi) büyük bir performans cezası olduğu için.
b) Çünkü bu sadece bir satır.
c) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error adresindeki çözüm, yığın bilgisini korumuyor gibi görünüyor.
//MyError class constructor
function MyError(msg){
this.__proto__.__proto__ = Error.apply(null, arguments);
};
kullanım örneği
http://jsfiddle.net/luciotato/xXyeB/
this.__proto__.__proto__
olduğu MyError.prototype.__proto__
, bu yüzden kuruyor __proto__
belirli yeni oluşturulan Hatası MyError ait tüm örnekleri için. MyError sınıfı özelliklerini ve yöntemlerini tutar ve ayrıca yeni Error özelliklerini (.stack dahil) __proto__
zincire koyar .
Yararlı yığın bilgisine sahip birden fazla MyError örneğiniz olamaz.
Ne yaptığını tam olarak anlamadıysanız bu çözümü kullanmayın this.__proto__.__proto__=
.
JavaScript İstisnalarının alt sınıfa zor gelmesi nedeniyle alt sınıfa katılmam. Sadece yeni bir İstisna sınıfı oluşturuyorum ve içinde bir Hata kullanıyorum. Konsoldaki özel özel durumum gibi görünecek şekilde Error.name özelliğini değiştirdim:
var InvalidInputError = function(message) {
var error = new Error(message);
error.name = 'InvalidInputError';
return error;
};
Yukarıdaki yeni istisna, normal bir Hata gibi atılabilir ve beklendiği gibi çalışacaktır, örneğin:
throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string
Uyarı: Yığın izi mükemmel değildir, çünkü sizi yeni Hatanın oluşturulduğu yere getirir ve attığınız yere getirmez. Bu, Chrome'da büyük bir sorun değil, çünkü doğrudan konsolda tam bir yığın izlemesi sağlıyor. Ancak, örneğin Firefox'ta daha sorunlu.
m = new InvalidInputError(); dontThrowMeYet(m);
m = new ...
o zaman kullanıyorum Promise.reject(m)
. Bu bir zorunluluk değildir, ancak kodun okunması daha kolaydır.
Mohsen'in cevabında belirtildiği gibi, ES6'da sınıfları kullanarak hataları genişletmek mümkündür. Çok daha kolay ve davranışları yerel hatalarla daha tutarlı ... ancak ES6 öncesi tarayıcıları desteklemeniz gerekiyorsa maalesef bunu tarayıcıda kullanmak basit bir mesele değil. Bunun nasıl uygulanabileceğine dair bazı notlar için aşağıya bakın, ancak bu arada diğer cevaplardan en iyi önerilerin bazılarını içeren nispeten basit bir yaklaşım öneriyorum:
function CustomError(message) {
//This is for future compatibility with the ES6 version, which
//would display a similar message if invoked without the
//`new` operator.
if (!(this instanceof CustomError)) {
throw new TypeError("Constructor 'CustomError' cannot be invoked without 'new'");
}
this.message = message;
//Stack trace in V8
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
else this.stack = (new Error).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.name = 'CustomError';
ES6'da bu kadar basit:
class CustomError extends Error {}
... ve ES6 sınıfları için desteği algılayabilirsiniz try {eval('class X{}')
, ancak ES6 sürümünü eski tarayıcılar tarafından yüklenen bir komut dosyasına dahil etmeye çalışırsanız bir sözdizimi hatası alırsınız. Bu nedenle, tüm tarayıcıları desteklemenin tek yolu, eval()
ES6'yı destekleyen tarayıcılar için dinamik olarak ayrı bir komut dosyası (ör. AJAX veya aracılığıyla ) yüklemek olacaktır. Diğer bir karmaşık nokta ise eval()
, projeniz için dikkate alınabilecek veya olmayabilecek tüm ortamlarda (İçerik Güvenliği Politikaları nedeniyle) desteklenmemesidir.
Şimdilik, yukarıdaki ilk yaklaşım ya da sadece Error
genişletmeye çalışmadan doğrudan kullanmak, ES6 olmayan tarayıcıları desteklemesi gereken kod için pratik olarak yapılabilecek en iyi şey gibi görünüyor.
Bazı insanların dikkate almak isteyebileceği başka bir yaklaşım daha vardır; bu, Object.setPrototypeOf()
özel hata türünüzün bir örneği olan ancak konsoldaki yerel bir hataya benzeyen ve daha çok davranan bir hata nesnesi oluşturmak için mümkün olan yerlerde kullanmaktır ( Ben'in cevabı sayesinde) tavsiye için). İşte bu yaklaşımı benim almak: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 . Ancak bir gün ES6'yı kullanabileceğimiz göz önüne alındığında, kişisel olarak bu yaklaşımın karmaşıklığının buna değdiğinden emin değilim.
Bu hakkı yapmanın yolu, yapıcıdan uygulamanın sonucunu döndürmenin yanı sıra, prototipi her zamanki karmaşık javascripty yolunda ayarlamaktır:
function MyError() {
var tmp = Error.apply(this, arguments);
tmp.name = this.name = 'MyError'
this.stack = tmp.stack
this.message = tmp.message
return this
}
var IntermediateInheritor = function() {}
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor()
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 ...>
Bu noktada bunu yapmanın tek yolu (biraz yineledim)
stack
ve message
yer almayan MyError
veİlk sorun, bu yanıttaki numarayı kullanarak tüm numaralandırılamayan hata özellikleri aracılığıyla yinelenerek düzeltilebilir: Bir nesnenin numaralandırılamayan devralınan özellik adlarını almak mümkün mü? , ancak bu <9 tarafından desteklenmez. İkinci sorun yığın izinde bu satırı yırtarak çözülebilir, ancak bunu nasıl güvenli bir şekilde yapacağımdan emin değilim (belki sadece ikinci e.stack.toString () ?? satırını kaldırarak).
Snippet hepsini gösterir.
function add(x, y) {
if (x && y) {
return x + y;
} else {
/**
*
* the error thrown will be instanceof Error class and InvalidArgsError also
*/
throw new InvalidArgsError();
// throw new Invalid_Args_Error();
}
}
// Declare custom error using using Class
class Invalid_Args_Error extends Error {
constructor() {
super("Invalid arguments");
Error.captureStackTrace(this);
}
}
// Declare custom error using Function
function InvalidArgsError(message) {
this.message = `Invalid arguments`;
Error.captureStackTrace(this);
}
// does the same magic as extends keyword
Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);
try{
add(2)
}catch(e){
// true
if(e instanceof Error){
console.log(e)
}
// true
if(e instanceof InvalidArgsError){
console.log(e)
}
}
Bir adım geriye gidip neden bunu yapmak istediğini düşünürdüm? Bence önemli olan farklı hatalarla farklı başa çıkmaktır.
Örneğin, Python'da catch deyimini yalnızca yakalamak için kısıtlayabilirsiniz MyValidationError
ve belki de javascript'te benzer bir şey yapmak isteyebilirsiniz.
catch (MyValidationError e) {
....
}
Javascript ile bunu yapamazsınız. Sadece bir tane yakalama bloğu olacak. Türünü belirlemek için hata üzerinde bir if ifadesi kullanmanız gerekir.
catch(e) {
if(isMyValidationError(e)) {
...
} else {
// maybe rethrow?
throw e;
}
}
Bunun yerine bir tür, mesaj ve uygun gördüğünüz diğer özellikleri ile ham bir nesne atmak düşünüyorum.
throw { type: "validation", message: "Invalid timestamp" }
Ve hatayı yakaladığınızda:
catch(e) {
if(e.type === "validation") {
// handle error
}
// re-throw, or whatever else
}
error.stack
, standart kalıp olacak onunla değil iş, vs vs. yolu bir hata örneğine özelliklerini eklemek olacaktır örneğin daha iyi birvar e = new Error(); e.type = "validation"; ...
Bu George Bailey'in cevabına dayanıyor , ancak orijinal fikri genişletiyor ve basitleştiriyor. CoffeeScript ile yazılmıştır, ancak JavaScript'e dönüştürmek kolaydır. Fikir, Bailey'nin özel hatasını, onu saran bir dekoratörle genişleterek kolayca yeni özel hatalar oluşturmanıza izin vermektir.
Not: Bu yalnızca V8'de çalışır. Error.captureStackTrace
Diğer ortamlarda destek yoktur .
Dekoratör, hata türü için bir ad alır ve hata iletisi alan bir işlev döndürür ve hata adını ekler.
CoreError = (@message) ->
@constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace @, @constructor
@name = @constructor.name
BaseError = (type) ->
(message) -> new CoreError "#{ type }Error: #{ message }"
Artık yeni hata türleri oluşturmak çok kolay.
StorageError = BaseError "Storage"
SignatureError = BaseError "Signature"
Eğlenmek için, artık SignatureError
çok fazla argümanla çağrılırsa bir işlev atan bir işlev tanımlayabilirsiniz .
f = -> throw SignatureError "too many args" if arguments.length
Bu oldukça iyi test edildi ve V8'de mükemmel bir şekilde çalışıyor, geri izleme, konum vb.
Not: new
Özel bir hata oluştururken kullanmak isteğe bağlıdır.
hata performanslarını umursamıyorsanız, yapabileceğiniz en küçük şey budur
Object.setPrototypeOf(MyError.prototype, Error.prototype)
function MyError(message) {
const error = new Error(message)
Object.setPrototypeOf(error, MyError.prototype);
return error
}
sadece yeni MyError (mesaj) olmadan kullanabilirsiniz
Oluşturucu Hatası çağrıldıktan sonra prototipi değiştirerek çağrı ve mesajı ayarlamamız gerekmiyor
Mohsen'in ES6'da adı belirleyen harika bir cevabı var, ancak TypeScript kullanıyorsanız veya umarım gelecekte kamu ve özel sınıf alanları için bu teklifin bir teklif olarak 3. aşamayı geçtiğini ve yaptığını düşünüyorsanız ECMAScript / JavaScript'in bir parçası olarak 4. aşamaya geçtikten sonra bunun biraz daha kısa olduğunu bilmek isteyebilirsiniz. Aşama 3, tarayıcıların özellikleri uygulamaya başladığı yerdir, bu nedenle tarayıcınız destekliyorsa aşağıdaki kod işe yarayabilir. (Yeni Edge tarayıcı v81'de test edilmiş gibi görünüyor). Bu şu anda kararsız bir özellik olsa da dikkatli olun ve dikkatli kullanılmalıdır ve kararsız özellikler konusunda her zaman tarayıcı desteğini kontrol etmelisiniz. Bu yayın, tarayıcıların bunu destekleyebileceği gelecekteki sakinler içindir. Destek kontrolünü kontrol etmek MDN'yive kullanabilir miyim . Şu anda tarayıcı pazarında% 66 destek var, ancak o kadar da iyi değil, bu yüzden şimdi kullanmak istiyorsanız ve Babel gibi bir transpiler veya TypeScript gibi bir şey kullanmak istemiyorsanız .
class EOFError extends Error {
name="EOFError"
}
throw new EOFError("Oops errored");
Bunu, atıldığında adının kaydedilmeyeceği isimsiz bir hatayla karşılaştırın.
class NamelessEOFError extends Error {}
throw new NamelessEOFError("Oops errored");
this.name = 'MyError'
İşlevin dışını taşıyabilir ve olarak değiştirebilirsinizMyError.prototype.name = 'MyError'
.