Satu-satunya objek Galat bidang standar miliki adalah message
properti. (Lihat MDN , atau Spesifikasi Bahasa EcmaScript, bagian 15.11) Yang lainnya adalah platform spesifik.
Sebagian besar lingkungan mengatur stack
properti, tetapi fileName
dan lineNumber
praktis tidak berguna untuk digunakan dalam warisan.
Jadi, pendekatan minimalis adalah:
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
Anda dapat mengendus tumpukan, melepaskan elemen yang tidak diinginkan darinya dan mengekstrak informasi seperti fileName dan lineNumber, tetapi untuk melakukannya diperlukan informasi tentang platform yang saat ini dijalankan JavaScript. Sebagian besar kasus yang tidak perlu - dan Anda dapat melakukannya di rumah sakit jika Anda benar-benar menginginkannya.
Safari adalah pengecualian penting. Tidak ada stack
properti, tetapi throw
set kata kunci sourceURL
dan line
properti objek yang dilemparkan. Hal-hal itu dijamin benar.
Kasus pengujian yang saya gunakan dapat ditemukan di sini: JavaScript membuat perbandingan objek Kesalahan .
this.name = 'MyError'
luar fungsi dan mengubahnya keMyError.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
juga.this
, kan?Dalam ES6:
sumber
sumber
var supportsClasses = false; try {eval('class X{}'); supportsClasses = true;} catch (e) {}
this.name = this.constructor.name;
saja.Pendeknya:
Jika Anda menggunakan ES6 tanpa transpiler :
Jika Anda menggunakan Babel transpiler :
Opsi 1: gunakan babel-plugin-transform-builtin-extended
Opsi 2: lakukan sendiri (terinspirasi dari perpustakaan yang sama)
Jika Anda menggunakan ES5 murni :
Alternatif: gunakan kerangka kerja Classtrophobic
Penjelasan:
Mengapa memperluas kelas Kesalahan menggunakan ES6 dan Babel merupakan masalah?
Karena turunan CustomError tidak lagi dikenali.
Bahkan, dari dokumentasi resmi Babel, Anda tidak dapat memperpanjang built-in kelas JavaScript seperti
Date
,Array
,DOM
atauError
.Masalahnya dijelaskan di sini:
Bagaimana dengan jawaban SO lainnya?
Semua jawaban yang diberikan memperbaiki
instanceof
masalah tetapi Anda kehilangan kesalahan regulerconsole.log
:Sedangkan menggunakan metode yang disebutkan di atas, tidak hanya Anda memperbaiki
instanceof
masalah tetapi Anda juga menyimpan kesalahan rutinconsole.log
:sumber
class CustomError extends Error { /* ... */}
tidak benar menangani argumen khusus vendor (lineNumber
, dll), 'Memperluas Kesalahan dalam Javascript dengan sintaks ES6' adalah spesifik Babel, menggunakan solusi ES5 Andaconst
dan tidak menangani argumen khusus.console.log(new CustomError('test') instanceof CustomError);// false
itu benar pada saat penulisan tetapi sekarang telah diselesaikan. Sebenarnya masalah yang ditautkan dalam jawaban telah diselesaikan dan kami dapat menguji perilaku yang benar di sini dan dengan menempelkan kode dalam REPL dan melihat bagaimana hal itu ditransformasikan dengan benar untuk menjadi contoh dengan rantai prototipe yang benar.Sunting: Silakan baca komentar. Ternyata ini hanya berfungsi dengan baik di V8 (Chrome / Node.JS) Maksud saya adalah untuk memberikan solusi lintas-browser, yang akan bekerja di semua browser, dan menyediakan jejak tumpukan di mana ada dukungan.
Sunting: Saya membuat Wiki Komunitas ini untuk memungkinkan pengeditan lebih lanjut.
Solusi untuk V8 (Chrome / Node.JS), berfungsi di Firefox, dan dapat dimodifikasi agar berfungsi sebagian besar dengan benar di IE. (lihat akhir posting)
Posting asli pada "Tunjukkan kodenya!"
Versi pendek:
Saya tetap
this.constructor.prototype.__proto__ = Error.prototype
di dalam fungsi untuk menyimpan semua kode. Tetapi Anda juga dapat menggantithis.constructor
denganUserError
dan itu memungkinkan Anda untuk memindahkan kode ke luar fungsi, sehingga hanya dipanggil sekali.Jika Anda pergi ke rute itu, pastikan Anda menelepon saluran itu sebelum pertama kali Anda melempar
UserError
.Peringatan itu tidak menerapkan fungsi, karena fungsi dibuat terlebih dahulu, apa pun urutannya. Dengan demikian, Anda dapat memindahkan fungsi ke akhir file, tanpa masalah.
Kompatibilitas Browser
Bekerja di Firefox dan Chrome (dan Node.JS) serta memenuhi semua janji.
Internet Explorer gagal sebagai berikut
Kesalahan tidak harus
err.stack
dimulai dengan, jadi "itu bukan salahku".Error.captureStackTrace(this, this.constructor)
tidak ada sehingga Anda perlu melakukan hal lain sepertitoString
tidak ada lagi saat Anda subkelasError
. Jadi, Anda juga perlu menambahkan.IE tidak akan mempertimbangkan
UserError
menjadiinstanceof Error
kecuali Anda menjalankan beberapa waktu sebelum Andathrow UserError
sumber
Error.call(this)
memang tidak melakukan apa-apa karena mengembalikan kesalahan daripada memodifikasithis
.UserError.prototype = Error.prototype
menyesatkan. Ini tidak melakukan warisan, ini membuat mereka kelas yang sama .Object.setPrototypeOf(this.constructor.prototype, Error.prototype)
lebih disukaithis.constructor.prototype.__proto__ = Error.prototype
, setidaknya untuk browser saat ini.Untuk menghindari pelat ketel untuk setiap jenis kesalahan yang berbeda, saya menggabungkan kebijaksanaan beberapa solusi ke dalam
createErrorType
fungsi:Kemudian Anda dapat mendefinisikan tipe kesalahan baru dengan mudah sebagai berikut:
sumber
this.name = name;
?name
sudah diatur pada prototipe, itu tidak perlu lagi. Saya menghapusnya. Terima kasih!Pada 2018 , saya pikir ini adalah cara terbaik; yang mendukung IE9 + dan browser modern.
UPDATE : Lihat tes ini dan repo untuk perbandingan tentang implementasi yang berbeda.
Waspadalah juga bahwa
__proto__
properti tidak digunakan lagi yang banyak digunakan dalam jawaban lain.sumber
setPrototypeOf()
? Setidaknya menurut MDN, biasanya tidak disarankan untuk menggunakannya jika Anda dapat melakukan hal yang sama dengan hanya menyetel.prototype
properti pada konstruktor (seperti yang Anda lakukan dielse
blok untuk peramban yang tidak memilikisetPrototypeOf
).setPrototypeOf
. Tetapi jika Anda masih membutuhkannya (seperti yang diminta OP), Anda harus menggunakan metodologi bawaan. Seperti yang ditunjukkan MDN, ini dianggap sebagai cara yang tepat untuk mengatur prototipe suatu objek. Dengan kata lain, MDN mengatakan jangan mengubah prototipe (karena itu mempengaruhi kinerja dan optimasi) tetapi jika Anda harus, gunakansetPrototypeOf
.CustomError.prototype = Object.create(Error.prototype)
). Juga,Object.setPrototypeOf(CustomError, Error.prototype)
adalah menetapkan prototipe konstruktor itu sendiri daripada menentukan prototipe untuk contoh baruCustomError
. Bagaimanapun, pada tahun 2016 saya pikir sebenarnya ada cara yang lebih baik untuk memperpanjang kesalahan, meskipun saya masih mencari cara untuk menggunakannya bersama Babel: github.com/loganfsmyth/babel-plugin-transform-builtin-extend/…CustomError.prototype = Object.create(Error.prototype)
juga mengubah prototipe. Anda harus mengubahnya karena tidak ada logika extended / inherit di ES5. Saya yakin plugin babel yang Anda sebutkan melakukan hal serupa.Object.setPrototypeOf
tidak masuk akal di sini, setidaknya tidak dengan cara Anda menggunakannya: gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348 . Mungkin Anda bermaksud menulisObject.setPrototypeOf(CustomError.prototype, Error.prototype)
- itu akan membuat sedikit lebih masuk akal (meskipun masih tidak memberikan manfaat hanya dengan menetapkanCustomError.prototype
).Demi kelengkapan - hanya karena tidak ada jawaban sebelumnya yang menyebutkan metode ini - jika Anda bekerja dengan Node.js dan tidak perlu peduli dengan kompatibilitas browser, efek yang diinginkan cukup mudah dicapai dengan built in
inherits
dariutil
modul ( docs resmi di sini ).Misalnya, anggap Anda ingin membuat kelas kesalahan khusus yang menggunakan kode kesalahan sebagai argumen pertama dan pesan kesalahan sebagai argumen kedua:
mengajukan custom-error.js :
Sekarang Anda dapat instantiate dan lulus / lemparkan
CustomError
:Perhatikan bahwa, dengan cuplikan ini, jejak tumpukan akan memiliki nama dan baris file yang benar, dan instance kesalahan akan memiliki nama yang benar!
Ini terjadi karena penggunaan
captureStackTrace
metode, yang membuatstack
properti pada objek target (dalam hal ini, yangCustomError
sedang dipakai). Untuk detail lebih lanjut tentang cara kerjanya, lihat dokumentasi di sini .sumber
this.message = this.message;
apakah ini salah atau masih ada hal-hal gila yang tidak saya ketahui tentang JS?Jawaban Crescent Fresh jawaban sangat menyesatkan. Meskipun peringatannya tidak valid, ada batasan lain yang tidak dia tangani.
Pertama, alasan dalam paragraf "Peringatan:" Crescent tidak masuk akal. Penjelasan menyiratkan bahwa pengkodean "sekelompok if (instance error MyError) lain ..." entah bagaimana memberatkan atau bertele-tele dibandingkan dengan beberapa pernyataan tangkapan. Banyak contoh pernyataan dalam satu blok tangkap sama ringkasnya dengan banyak pernyataan tangkap - kode bersih dan ringkas tanpa trik. Ini adalah cara yang bagus untuk meniru penanganan error subtipe-spesifik Java yang bisa dibuang.
WRT "muncul properti pesan dari subkelas tidak diatur", itu tidak terjadi jika Anda menggunakan subkelas Kesalahan dibangun dengan benar. Untuk membuat subclass ErrorX Anda sendiri, cukup salin blok kode yang diawali dengan "var MyError =", mengubah satu kata "MyError" menjadi "ErrorX". (Jika Anda ingin menambahkan metode khusus ke subkelas Anda, ikuti teks sampel).
Batasan nyata dan signifikan dari subclass kesalahan JavaScript adalah bahwa untuk implementasi atau debugers JavaScript yang melacak dan melaporkan jejak stack dan lokasi-instantiasi, seperti FireFox, lokasi dalam implementasi subclass Error Anda sendiri akan dicatat sebagai titik instantiasi dari kelas, sedangkan jika Anda menggunakan Kesalahan langsung, itu akan menjadi lokasi di mana Anda menjalankan "Kesalahan baru (...)"). Pengguna IE mungkin tidak akan pernah memperhatikan, tetapi pengguna Fire Bug pada FF akan melihat nilai nama file dan nomor baris yang tidak berguna yang dilaporkan di samping Kesalahan ini, dan harus menelusuri jejak tumpukan ke elemen # 1 untuk menemukan lokasi instantiasi nyata.
sumber
Crescent Fresh's
telah dihapus!Bagaimana dengan solusi ini?
Alih-alih melempar Kesalahan khusus Anda menggunakan:
Anda akan membungkus objek Kesalahan (seperti dekorator):
Ini memastikan semua atribut benar, seperti tumpukan, fileName lineNumber, dan lain-lain.
Yang harus Anda lakukan adalah menyalin atribut, atau menentukan getter untuknya. Berikut ini adalah contoh menggunakan getter (IE9):
sumber
new MyErr (arg1, arg2, new Error())
dan dalam konstruktor MyErr kita gunakanObject.assign
untuk menetapkan properti arg terakhir kethis
Seperti yang dikatakan beberapa orang, cukup mudah dengan ES6:
Jadi saya mencobanya di dalam aplikasi saya, (Angular, Typescript) dan ternyata tidak berhasil. Setelah beberapa waktu saya telah menemukan bahwa masalahnya berasal dari Script: O
Lihat https://github.com/Microsoft/TypeScript/issues/13965
Ini sangat mengganggu karena jika Anda melakukannya:
Di node atau langsung ke browser Anda, ia akan menampilkan:
Custom error
Cobalah untuk menjalankannya dengan Typescript di proyek Anda di pada taman bermain Typecript, itu akan menampilkan
Basic error
...Solusinya adalah dengan melakukan hal berikut:
sumber
Solusi saya lebih sederhana daripada jawaban lain yang diberikan dan tidak memiliki kelemahan.
Ini mempertahankan rantai prototipe Kesalahan dan semua properti pada Kesalahan tanpa perlu pengetahuan khusus tentang mereka. Sudah diuji di Chrome, Firefox, Node, dan IE11.
Satu-satunya batasan adalah entri tambahan di bagian atas tumpukan panggilan. Tapi itu mudah diabaikan.
Berikut ini contoh dengan dua parameter khusus:
Contoh penggunaan:
Untuk lingkungan yang memerlukan polyfil dari setPrototypeOf:
sumber
throw CustomError('err')
alih-alihthrow new CustomError('err')
Dalam contoh di atas
Error.apply
(jugaError.call
) tidak melakukan apa pun untuk saya (Firefox 3.6 / Chrome 5). Solusi yang saya gunakan adalah:sumber
Di Node seperti yang orang lain katakan, itu sederhana:
sumber
Saya hanya ingin menambahkan apa yang sudah dikatakan orang lain:
Untuk memastikan bahwa kelas kesalahan khusus muncul dengan benar di jejak tumpukan, Anda perlu mengatur properti nama prototipe kelas kesalahan ke properti nama kelas kesalahan kustom. Inilah yang saya maksud:
Jadi contoh lengkapnya adalah:
Ketika semua dikatakan dan dilakukan, Anda melempar pengecualian baru Anda dan sepertinya ini (saya malas mencoba ini di alat dev chrome):
sumber
2 sen saya:
Mengapa ada jawaban lain?
a) Karena mengakses
Error.stack
properti (seperti dalam beberapa jawaban) memiliki penalti kinerja yang besar.b) Karena hanya satu baris.
c) Karena solusi di https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error tampaknya tidak mempertahankan informasi tumpukan.
contoh penggunaan
http://jsfiddle.net/luciotato/xXyeB/
Apa fungsinya?
this.__proto__.__proto__
adalahMyError.prototype.__proto__
, jadi itu mengatur__proto__
UNTUK SEMUA INSTAN MyError ke Kesalahan khusus yang baru dibuat. Itu membuat properti dan metode kelas MyError dan juga menempatkan properti Kesalahan baru (termasuk .stack) di__proto__
rantai.Masalah yang jelas:
Anda tidak dapat memiliki lebih dari satu instance MyError dengan info tumpukan berguna.
Jangan gunakan solusi ini jika Anda tidak sepenuhnya mengerti apa yang
this.__proto__.__proto__=
terjadi.sumber
Karena Pengecualian JavaScript sulit untuk sub-kelas, saya tidak sub-kelas. Saya baru saja membuat kelas Pengecualian baru dan menggunakan Kesalahan di dalamnya. Saya mengubah properti Error.name sehingga sepertinya pengecualian khusus pada konsol:
Pengecualian baru di atas dapat dilemparkan seperti Kesalahan biasa dan akan berfungsi seperti yang diharapkan, misalnya:
Peringatan: jejak tumpukan tidak sempurna, karena akan membawa Anda ke tempat Kesalahan baru dibuat dan bukan ke tempat Anda membuang. Ini bukan masalah besar di Chrome karena memberi Anda jejak tumpukan penuh langsung di konsol. Tapi itu lebih bermasalah di Firefox, misalnya.
sumber
m = new InvalidInputError(); dontThrowMeYet(m);
m = new ...
kemudianPromise.reject(m)
. Bukan keharusan, tetapi kodenya lebih mudah dibaca.Seperti yang ditunjukkan dalam jawaban Mohsen, dalam ES6 dimungkinkan untuk memperluas kesalahan menggunakan kelas. Ini jauh lebih mudah dan perilaku mereka lebih konsisten dengan kesalahan asli ... tapi sayangnya itu bukan masalah sederhana untuk menggunakan ini di browser jika Anda perlu mendukung browser pra-ES6. Lihat di bawah untuk beberapa catatan tentang bagaimana hal itu dapat diterapkan, tetapi sementara itu saya menyarankan pendekatan yang relatif sederhana yang menggabungkan beberapa saran terbaik dari jawaban lain:
Dalam ES6 sesederhana:
... dan Anda dapat mendeteksi dukungan untuk kelas ES6
try {eval('class X{}')
, tetapi Anda akan mendapatkan kesalahan sintaksis jika Anda mencoba untuk memasukkan versi ES6 dalam skrip yang dimuat oleh browser yang lebih lama. Jadi satu-satunya cara untuk mendukung semua browser adalah dengan memuat skrip terpisah secara dinamis (misalnya melalui AJAX ataueval()
) untuk browser yang mendukung ES6. Komplikasi lebih lanjut adalah yangeval()
tidak didukung di semua lingkungan (karena Kebijakan Keamanan Konten), yang mungkin atau mungkin tidak menjadi pertimbangan untuk proyek Anda.Jadi untuk saat ini, baik pendekatan pertama di atas atau hanya menggunakan
Error
secara langsung tanpa mencoba memperluas tampaknya menjadi yang terbaik yang secara praktis dapat dilakukan untuk kode yang perlu mendukung browser non-ES6.Ada satu pendekatan lain yang beberapa orang mungkin ingin mempertimbangkan, yaitu menggunakan di
Object.setPrototypeOf()
mana tersedia untuk membuat objek kesalahan yang merupakan contoh dari jenis kesalahan kustom Anda tetapi yang terlihat dan berperilaku lebih seperti kesalahan asli di konsol (berkat jawaban Ben untuk rekomendasi). Inilah pendapat saya tentang pendekatan itu: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 . Tetapi mengingat bahwa suatu hari kita akan dapat hanya menggunakan ES6, secara pribadi saya tidak yakin kompleksitas pendekatan itu sepadan.sumber
Cara untuk melakukan ini dengan benar adalah dengan mengembalikan hasil berlaku dari konstruktor, serta menetapkan prototipe dengan cara javascripty rumit yang biasa:
Satu-satunya masalah dengan cara melakukannya pada saat ini (saya sudah mengulanginya sedikit) adalah itu
stack
danmessage
tidak termasuk dalamMyError
danMasalah pertama dapat diperbaiki dengan mengulangi semua properti kesalahan yang tidak dapat dihitung menggunakan trik dalam jawaban ini: Apakah mungkin untuk mendapatkan nama properti yang diwarisi dari objek yang tidak dapat dihitung? , tetapi ini tidak didukung oleh yaitu <9. Masalah kedua dapat diselesaikan dengan merobek garis itu di jejak tumpukan, tapi saya tidak yakin bagaimana melakukannya dengan aman (mungkin hanya menghapus baris kedua dari e.stack.toString () ??).
sumber
Cuplikan menunjukkan semuanya.
sumber
Saya akan mengambil langkah mundur dan mempertimbangkan mengapa Anda ingin melakukan itu? Saya pikir intinya adalah menangani kesalahan yang berbeda secara berbeda.
Misalnya, dalam Python, Anda dapat membatasi pernyataan tangkapan hanya tangkapan
MyValidationError
, dan mungkin Anda ingin dapat melakukan hal serupa di javascript.Anda tidak dapat melakukan ini dalam javascript. Hanya akan ada satu blok penangkap. Anda seharusnya menggunakan pernyataan if pada kesalahan untuk menentukan jenisnya.
catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }
Saya pikir saya akan membuang objek mentah dengan tipe, pesan, dan properti lain yang Anda inginkan.
Dan ketika Anda menangkap kesalahan:
sumber
error.stack
, perkakas standar tidak akan berfungsi dengannya, dll. Cara yang lebih baik adalah menambahkan properti ke instance kesalahan, misalnyavar e = new Error(); e.type = "validation"; ...
Penghias Kesalahan Kustom
Ini didasarkan pada jawaban George Bailey , tetapi memperluas dan menyederhanakan ide aslinya. Ini ditulis dalam CoffeeScript, tetapi mudah dikonversi ke JavaScript. Idenya adalah memperpanjang kesalahan kustom Bailey dengan dekorator yang membungkusnya, memungkinkan Anda untuk membuat kesalahan khusus baru dengan mudah.
Catatan: Ini hanya akan berfungsi di V8. Tidak ada dukungan untuk
Error.captureStackTrace
di lingkungan lain.Menetapkan
Dekorator mengambil nama untuk jenis kesalahan, dan mengembalikan fungsi yang mengambil pesan kesalahan, dan menyertakan nama kesalahan.
Menggunakan
Sekarang mudah untuk membuat jenis kesalahan baru.
Untuk bersenang-senang, Anda sekarang dapat mendefinisikan fungsi yang melempar a
SignatureError
jika dipanggil dengan terlalu banyak argumen.Ini telah diuji dengan cukup baik dan tampaknya berfungsi dengan baik pada V8, mempertahankan traceback, posisi, dll.
Catatan: Menggunakan
new
adalah opsional saat membuat kesalahan khusus.sumber
jika Anda tidak peduli dengan kesalahan karena ini adalah yang terkecil yang dapat Anda lakukan
Anda dapat menggunakannya tanpa hanya MyError (pesan) baru
Dengan mengubah prototipe setelah Kesalahan konstruktor dipanggil, kita tidak perlu mengatur callstack dan pesan
sumber
Mohsen memiliki jawaban hebat di atas dalam ES6 yang menetapkan nama, tetapi jika Anda menggunakan TypeScript atau jika Anda hidup di masa depan di mana semoga proposal untuk bidang kelas publik dan swasta ini telah bergerak melewati tahap 3 sebagai proposal dan membuatnya ke tahap 4 sebagai bagian dari ECMAScript / JavaScript maka Anda mungkin ingin tahu ini kemudian hanya sedikit lebih pendek. Tahap 3 adalah tempat browser mulai mengimplementasikan fitur, jadi jika browser Anda mendukungnya, kode di bawah ini mungkin berfungsi. (Diuji di browser Edge baru v81 tampaknya berfungsi dengan baik). Berhati-hatilah meskipun ini adalah fitur yang tidak stabil saat ini dan harus digunakan dengan hati-hati dan Anda harus selalu memeriksa dukungan browser pada fitur yang tidak stabil. Posting ini terutama untuk para penghuni masa depan ketika browser mungkin mendukung ini. Untuk memeriksa dukungan, periksa MDNdan Dapatkah saya menggunakan . Saat ini mendapat dukungan 66% di seluruh pasar browser yang ada di sana tetapi tidak terlalu bagus jadi jika Anda benar-benar ingin menggunakannya sekarang dan tidak ingin menunggu menggunakan transpiler seperti Babel atau sesuatu seperti TypeScript .
Bandingkan ini dengan kesalahan tanpa nama yang ketika dilempar tidak akan mencatat namanya.
sumber