Jenis pengecualian khusus

224

Bisakah saya menentukan jenis khusus untuk pengecualian yang ditentukan pengguna dalam JavaScript? Jika demikian, bagaimana saya melakukannya?

Manki
sumber
3
Awas. Menurut JavaScript dalam 10 Menit Anda tidak akan mendapatkan jejak tumpukan jika Anda membuang nilai yang tidak dikotak.
Janus Troelsen
exceptionionsjs.com menyediakan kemampuan untuk membuat pengecualian khusus dan memberikan beberapa pengecualian yang hilang termasuk ArgumentException dan NotImplemented secara default.
Steven Wexler

Jawaban:

232

Dari WebReference :

throw { 
  name:        "System Error", 
  level:       "Show Stopper", 
  message:     "Error detected. Please contact the system administrator.", 
  htmlMessage: "Error detected. Please contact the <a href=\"mailto:[email protected]\">system administrator</a>.",
  toString:    function(){return this.name + ": " + this.message;} 
}; 
jon077
sumber
7
@ b.long Ada dalam "JavaScript: The Good Parts" (IMO buku hebat). Pratinjau Google Buku ini menunjukkan bagian: books.google.com/books?id=PXa2bby0oQ0C&pg=PA32&lpg=PA32
orip
11
Menambahkan metode toString akan membuatnya tampil bagus di konsol javascript. tanpa itu akan muncul seperti: Uncaught # <Object> dengan toString terlihat seperti: Uncaught System Error: Error terdeteksi. Silakan hubungi administrator sistem.
JDC
11
Ini tidak akan memungkinkan Anda menumpuk jejak kecuali Anda mewarisi dari Kesalahan
Luke H
Bagaimana Anda bisa memfilter dalam blok tangkap untuk hanya bekerja dengan kesalahan khusus ini?
Overdrivr
@overdrivr sesuatu seperti catch (e) { if (e instanceof TypeError) { … } else { throw e; } }⦃⦄ atau catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }⦃⦄.
sam boosalis
92

Anda harus membuat pengecualian khusus yang secara bawaan mewarisi dari Kesalahan. Sebagai contoh:

function InvalidArgumentException(message) {
    this.message = message;
    // Use V8's native method if available, otherwise fallback
    if ("captureStackTrace" in Error)
        Error.captureStackTrace(this, InvalidArgumentException);
    else
        this.stack = (new Error()).stack;
}

InvalidArgumentException.prototype = Object.create(Error.prototype);
InvalidArgumentException.prototype.name = "InvalidArgumentException";
InvalidArgumentException.prototype.constructor = InvalidArgumentException;

Ini pada dasarnya adalah versi yang disederhanakan dari apa yang disayangkan diposting di atas dengan perangkat tambahan yang berfungsi melacak jejak pada Firefox dan browser lainnya. Itu memenuhi tes yang sama yang dia posting:

Pemakaian:

throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");

Dan itu akan berperilaku diharapkan:

err instanceof InvalidArgumentException          // -> true
err instanceof Error                             // -> true
InvalidArgumentException.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err)               // -> true
err.constructor.name                             // -> InvalidArgumentException
err.name                                         // -> InvalidArgumentException
err.message                                      // -> Not yet...
err.toString()                                   // -> InvalidArgumentException: Not yet...
err.stack                                        // -> works fine!
keparat
sumber
80

Anda dapat menerapkan pengecualian Anda sendiri dan penanganannya misalnya seperti di sini:

// define exceptions "classes" 
function NotNumberException() {}
function NotPositiveNumberException() {}

// try some code
try {
    // some function/code that can throw
    if (isNaN(value))
        throw new NotNumberException();
    else
    if (value < 0)
        throw new NotPositiveNumberException();
}
catch (e) {
    if (e instanceof NotNumberException) {
        alert("not a number");
    }
    else
    if (e instanceof NotPositiveNumberException) {
        alert("not a positive number");
    }
}

Ada sintaks lain untuk menangkap pengecualian yang diketik, meskipun ini tidak akan berfungsi di setiap browser (misalnya tidak di IE):

// define exceptions "classes" 
function NotNumberException() {}
function NotPositiveNumberException() {}

// try some code
try {
    // some function/code that can throw
    if (isNaN(value))
        throw new NotNumberException();
    else
    if (value < 0)
        throw new NotPositiveNumberException();
}
catch (e if e instanceof NotNumberException) {
    alert("not a number");
}
catch (e if e instanceof NotPositiveNumberException) {
    alert("not a positive number");
}
Sergey Ilinsky
sumber
2
Situs web MSN membawa peringatan ini tentang tangkapan kondisi: Non-standar Fitur ini non-standar dan tidak berada di jalur standar. Jangan menggunakannya di situs produksi yang menghadap Web: itu tidak akan berfungsi untuk setiap pengguna. Mungkin juga ada ketidakcocokan besar antara implementasi dan perilaku dapat berubah di masa depan.
Lawrence Dol
40

Iya. Anda dapat melempar apa pun yang Anda inginkan: bilangan bulat, string, objek, apa pun. Jika Anda ingin melempar objek, maka cukup buat objek baru, sama seperti Anda akan membuatnya dalam keadaan lain, lalu melemparkannya. Referensi Javascript Mozilla memiliki beberapa contoh.

Rob Kennedy
sumber
26
function MyError(message) {
 this.message = message;
}

MyError.prototype = new Error;

Ini memungkinkan untuk penggunaan seperti ..

try {
  something();
 } catch(e) {
  if(e instanceof MyError)
   doSomethingElse();
  else if(e instanceof Error)
   andNowForSomethingCompletelyDifferent();
}
Morgan ARR Allen
sumber
Bukankah contoh singkat ini akan bekerja dengan cara yang persis sama bahkan jika Anda tidak mewarisi prototipe Kesalahan? Tidak jelas bagi saya apa yang menguntungkan Anda dalam contoh ini.
EleventyOne
1
Tidak, e instanceof Erroritu salah.
Morgan ARR Allen
Memang. Tetapi karena e instanceof MyErroritu benar, else if(e instanceof Error)pernyataan itu tidak akan pernah dievaluasi.
EleventyOne
Benar, ini hanya sebuah contoh bagaimana gaya coba-coba ini bekerja. Di mana else if(e instanceof Error)akan menjadi tangkapan terakhir. Kemungkinan diikuti oleh yang sederhana else(yang tidak saya sertakan). Agak seperti default:dalam pernyataan switch tetapi untuk kesalahan.
Morgan ARR Allen
15

Pendeknya:

Opsi 1: gunakan babel-plugin-transform-builtin-extended

Opsi 2: lakukan sendiri (terinspirasi dari perpustakaan yang sama)

    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);
  • Jika Anda menggunakan ES5 murni :

    function CustomError(message, fileName, lineNumber) {
      const 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: gunakan kerangka kerja Classtrophobic

Penjelasan:

Mengapa memperluas kelas Kesalahan menggunakan ES6 dan Babel merupakan masalah?

Karena turunan CustomError tidak lagi dikenali.

class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false

Bahkan, dari dokumentasi resmi Babel, Anda tidak dapat memperpanjang built-in kelas JavaScript seperti Date, Array, DOMatau Error.

Masalahnya dijelaskan di sini:

Bagaimana dengan jawaban SO lainnya?

Semua jawaban yang diberikan memperbaiki instanceofmasalah tetapi Anda kehilangan kesalahan reguler console.log:

console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵    at CustomError (<anonymous>:4:19)↵    at <anonymous>:1:5"}

Sedangkan menggunakan metode yang disebutkan di atas, tidak hanya Anda memperbaiki instanceofmasalah tetapi Anda juga menyimpan kesalahan rutin console.log:

console.log(new CustomError('test'));
// output:
// Error: test
//     at CustomError (<anonymous>:2:32)
//     at <anonymous>:1:5
JBE
sumber
11

Di sini adalah bagaimana Anda dapat membuat kesalahan khusus dengan sepenuhnya identik dengan Errorperilaku asli. Teknik ini hanya berfungsi di Chrome dan node.js untuk saat ini. Saya juga tidak akan merekomendasikan untuk menggunakannya jika Anda tidak mengerti apa fungsinya.

Error.createCustromConstructor = (function() {

    function define(obj, prop, value) {
        Object.defineProperty(obj, prop, {
            value: value,
            configurable: true,
            enumerable: false,
            writable: true
        });
    }

    return function(name, init, proto) {
        var CustomError;
        proto = proto || {};
        function build(message) {
            var self = this instanceof CustomError
                ? this
                : Object.create(CustomError.prototype);
            Error.apply(self, arguments);
            Error.captureStackTrace(self, CustomError);
            if (message != undefined) {
                define(self, 'message', String(message));
            }
            define(self, 'arguments', undefined);
            define(self, 'type', undefined);
            if (typeof init == 'function') {
                init.apply(self, arguments);
            }
            return self;
        }
        eval('CustomError = function ' + name + '() {' +
            'return build.apply(this, arguments); }');
        CustomError.prototype = Object.create(Error.prototype);
        define(CustomError.prototype, 'constructor', CustomError);
        for (var key in proto) {
            define(CustomError.prototype, key, proto[key]);
        }
        Object.defineProperty(CustomError.prototype, 'name', { value: name });
        return CustomError;
    }

})();

Sebagai hasil kita dapatkan

/**
 * name   The name of the constructor name
 * init   User-defined initialization function
 * proto  It's enumerable members will be added to 
 *        prototype of created constructor
 **/
Error.createCustromConstructor = function(name, init, proto)

Maka Anda dapat menggunakannya seperti ini:

var NotImplementedError = Error.createCustromConstructor('NotImplementedError');

Dan gunakan NotImplementedErrorseperti yang Anda inginkan Error:

throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError('Not yet...');

Dan itu akan berperilaku diharapkan:

err instanceof NotImplementedError               // -> true
err instanceof Error                             // -> true
NotImplementedError.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err)               // -> true
err.constructor.name                             // -> NotImplementedError
err.name                                         // -> NotImplementedError
err.message                                      // -> Not yet...
err.toString()                                   // -> NotImplementedError: Not yet...
err.stack                                        // -> works fine!

Catatan, itu error.stackberfungsi absolut dengan benar dan tidak akan menyertakan NotImplementedErrorpanggilan konstruktor (terima kasih kepada v8 Error.captureStackTrace()).

Catatan. Ada yang jelek eval(). Satu-satunya alasan digunakan adalah untuk mendapatkan yang benar err.constructor.name. Jika Anda tidak membutuhkannya, Anda dapat sedikit menyederhanakan semuanya.

dikecewakan
sumber
2
Error.apply(self, arguments)adalah ditentukan tidak bekerja . Saya sarankan untuk menyalin jejak stack yang kompatibel dengan cross-browser.
Kornel
11

Saya sering menggunakan pendekatan dengan pewarisan prototypal. Overriding toString()memberi Anda keuntungan bahwa alat-alat seperti Firebug akan mencatat informasi aktual alih-alih [object Object]ke konsol untuk pengecualian yang tidak tertangkap.

Gunakan instanceofuntuk menentukan jenis pengecualian.

main.js

// just an exemplary namespace
var ns = ns || {};

// include JavaScript of the following
// source files here (e.g. by concatenation)

var someId = 42;
throw new ns.DuplicateIdException('Another item with ID ' +
    someId + ' has been created');
// Firebug console:
// uncaught exception: [Duplicate ID] Another item with ID 42 has been created

Exception.js

ns.Exception = function() {
}

/**
 * Form a string of relevant information.
 *
 * When providing this method, tools like Firebug show the returned 
 * string instead of [object Object] for uncaught exceptions.
 *
 * @return {String} information about the exception
 */
ns.Exception.prototype.toString = function() {
    var name = this.name || 'unknown';
    var message = this.message || 'no description';
    return '[' + name + '] ' + message;
};

DuplicateIdException.js

ns.DuplicateIdException = function(message) {
    this.name = 'Duplicate ID';
    this.message = message;
};

ns.DuplicateIdException.prototype = new ns.Exception();
Matthias
sumber
8

ES6

Dengan kelas baru dan perpanjang kata kunci, sekarang jauh lebih mudah:

class CustomError extends Error {
  constructor(message) {
    super(message);
    //something
  }
}
Brunno
sumber
7

Gunakan pernyataan melempar .

JavaScript tidak peduli apa jenis pengecualiannya (seperti halnya Java). JavaScript hanya pemberitahuan, ada pengecualian dan ketika Anda menangkapnya, Anda bisa "melihat" apa yang dikatakan "pengecualian".

Jika Anda memiliki berbagai jenis pengecualian yang harus Anda buang, saya sarankan untuk menggunakan variabel yang berisi string / objek dari pesan pengecualian yaitu. Di mana Anda membutuhkannya, gunakan "throw myException" dan dalam tangkapan, bandingkan pengecualian yang tertangkap dengan myException.

Xn0vv3r
sumber
1

Lihat contoh ini di MDN.

Jika Anda perlu mendefinisikan beberapa Kesalahan (uji kodenya di sini !):

function createErrorType(name, initFunction) {
    function E(message) {
        this.message = message;
        if (Error.captureStackTrace)
            Error.captureStackTrace(this, this.constructor);
        else
            this.stack = (new Error()).stack;
        initFunction && initFunction.apply(this, arguments);
    }
    E.prototype = Object.create(Error.prototype);
    E.prototype.name = name;
    E.prototype.constructor = E;
    return E;
}
var InvalidStateError = createErrorType(
    'InvalidStateError',
    function (invalidState, acceptedStates) {
        this.message = 'The state ' + invalidState + ' is invalid. Expected ' + acceptedStates + '.';
    });

var error = new InvalidStateError('foo', 'bar or baz');
function assert(condition) { if (!condition) throw new Error(); }
assert(error.message);
assert(error instanceof InvalidStateError);  
assert(error instanceof Error); 
assert(error.name == 'InvalidStateError');
assert(error.stack);
error.message;

Kode sebagian besar disalin dari: Apa cara yang baik untuk memperpanjang Kesalahan dalam JavaScript?

Peter Tseng
sumber
1

Alternatif jawaban asselin untuk digunakan dengan kelas ES2015

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}
Tuan Tsjolder
sumber
1
//create error object
var error = new Object();
error.reason="some reason!";

//business function
function exception(){
    try{
        throw error;
    }catch(err){
        err.reason;
    }
}

Sekarang kita atur menambahkan alasan atau properti apa pun yang kita inginkan ke objek kesalahan dan mengambilnya. Dengan membuat kesalahan lebih masuk akal.

mateen
sumber