ประเภทข้อยกเว้นที่กำหนดเอง


224

ฉันสามารถกำหนดประเภทที่กำหนดเองสำหรับข้อยกเว้นที่ผู้ใช้กำหนดใน JavaScript ได้หรือไม่ ถ้าเป็นเช่นนั้นฉันจะทำอย่างไร


3
ระวัง. ตามจาวาสคริปต์ใน 10 นาทีคุณจะไม่ได้รับการติดตามสแต็กหากคุณทิ้งค่าที่ไม่ได้ทำไว้
Janus Troelsen

ข้อยกเว้นให้ความสามารถในการสร้างข้อยกเว้นที่กำหนดเองและให้ข้อยกเว้นบางอย่างที่ขาดหายไปรวมถึง ArgumentException และ NotImplemented โดยค่าเริ่มต้น
Steven Wexler

คำตอบ:


232

จาก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:sysadmin@acme-widgets.com\">system administrator</a>.",
  toString:    function(){return this.name + ": " + this.message;} 
}; 

7
@ b.long อยู่ใน "JavaScript: The Good Parts" (หนังสือดีเยี่ยม IMO) ตัวอย่าง Google หนังสือนี้แสดงหัวข้อ: books.google.co.th/books?id=PXa2bby0oQ0C&pg=PA32&lpg=PA32
orip

11
การเพิ่มเมธอด toString จะทำให้การแสดงเป็นอย่างดีในคอนโซล javascript ไม่มีมันแสดงให้เห็นเช่น: Uncaught # <Object> ด้วย toString มันจะแสดงเช่น: Uncaught System Error: ตรวจพบข้อผิดพลาด กรุณาติดต่อผู้ดูแลระบบ
JDC

11
สิ่งนี้จะไม่อนุญาตให้คุณติดตามสแต็กเว้นแต่ว่าคุณจะสืบทอดมาจากข้อผิดพลาด
Luke H

คุณจะกรองภายใน catch block ให้ทำงานกับข้อผิดพลาดที่กำหนดเองนี้ได้อย่างไร
Overdrivr

@overdrivr บางอย่างเช่นcatch (e) { if (e instanceof TypeError) { … } else { throw e; } }⦃⦄หรือcatch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }⦃⦄
sam boosalis

92

คุณควรสร้างข้อยกเว้นแบบกำหนดเองที่ต้นแบบสืบทอดมาจากข้อผิดพลาด ตัวอย่างเช่น:

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;

นี่เป็นเวอร์ชั่นที่เรียบง่ายของสิ่งที่ถูกโพสต์ไว้ข้างต้นด้วยการปรับปรุงที่สแต็กติดตามทำงานบน Firefox และเบราว์เซอร์อื่น ๆ มันเป็นไปตามการทดสอบเดียวกันกับที่เขาโพสต์:

การใช้งาน:

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

และมันจะทำงานตามที่คาดไว้:

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!

80

คุณสามารถใช้ข้อยกเว้นของคุณเองและจัดการตัวอย่างเช่นที่นี่:

// 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");
    }
}

มีอีกซินแทกซ์สำหรับจับข้อยกเว้นที่พิมพ์แม้ว่าจะไม่ทำงานในทุกเบราว์เซอร์ (ตัวอย่างเช่นไม่ได้อยู่ใน 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");
}

2
เว็บไซต์ MSN มีคำเตือนเกี่ยวกับการจับเงื่อนไข: ไม่ใช่มาตรฐานคุณสมบัตินี้ไม่ได้มาตรฐานและไม่ได้อยู่ในมาตรฐาน ห้ามใช้กับไซต์ที่ผลิตซึ่งหันหน้าไปทางเว็บ: มันจะไม่ทำงานสำหรับผู้ใช้ทุกคน อาจมีความไม่ลงรอยกันระหว่างการใช้งานกับพฤติกรรมที่อาจมีการเปลี่ยนแปลงในอนาคต
Lawrence Dol

40

ใช่. คุณสามารถโยนสิ่งที่คุณต้องการ: จำนวนเต็มสตริงวัตถุอะไรก็ได้ หากคุณต้องการที่จะโยนวัตถุเพียงแค่สร้างวัตถุใหม่เช่นเดียวกับที่คุณจะสร้างวัตถุภายใต้สถานการณ์อื่นแล้วโยนมัน การอ้างอิง Javascript ของ Mozillaมีหลายตัวอย่าง


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

MyError.prototype = new Error;

นี้ช่วยให้การใช้งานเช่น ..

try {
  something();
 } catch(e) {
  if(e instanceof MyError)
   doSomethingElse();
  else if(e instanceof Error)
   andNowForSomethingCompletelyDifferent();
}

ตัวอย่างสั้น ๆ นี้จะไม่ทำงานในลักษณะเดียวกันแม้ว่าคุณจะไม่ได้รับต้นแบบของข้อผิดพลาดหรือไม่ ยังไม่ชัดเจนสำหรับฉันในสิ่งที่ทำให้คุณได้รับในตัวอย่างนี้
EleventyOne

1
ไม่e instanceof Errorจะเป็นเท็จ
Morgan ARR Allen

จริง แต่เนื่องจากe instanceof MyErrorจะเป็นจริงelse if(e instanceof Error)ข้อความจะไม่ถูกประเมิน
EleventyOne

ใช่นี่เป็นเพียงตัวอย่างของวิธีการลอง / จับสไตล์นี้ว่าทำงานอย่างไร ไหนelse if(e instanceof Error)จะจับสุดท้าย น่าจะตามมาด้วยความเรียบง่ายelse(ซึ่งฉันไม่ได้รวม) เรียงจากชอบdefault:ในคำสั่งเปลี่ยน แต่สำหรับข้อผิดพลาด
Morgan ARR Allen

15

ในระยะสั้น:

ตัวเลือก 1: ใช้babel-plugin-transform-builtin-expand

ตัวเลือกที่ 2: ทำด้วยตัวเอง (แรงบันดาลใจจากห้องสมุดเดียวกัน)

    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);
  • หากคุณใช้ES5 แท้ :

    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;
    }
  • ทางเลือก: ใช้Classtrophobic framework

คำอธิบาย:

เหตุใดการขยายคลาสข้อผิดพลาดโดยใช้ ES6 และ Babel จึงเป็นปัญหา

เนื่องจากอินสแตนซ์ของ CustomError ไม่ได้รับการยอมรับเช่นนี้อีกต่อไป

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

ในความเป็นจริงจากเอกสารอย่างเป็นทางการของบาเบลที่คุณไม่สามารถขยายตัวใด ๆ ในชั้นเรียนของ JavaScriptเช่นDate, Array, หรือDOMError

ปัญหาอธิบายไว้ที่นี่:

แล้วคำตอบ SO อื่น ๆ ล่ะ?

คำตอบที่ให้ทั้งหมดแก้ไขinstanceofปัญหา แต่คุณสูญเสียข้อผิดพลาดปกติconsole.log:

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

ในขณะที่ใช้วิธีการดังกล่าวข้างต้นไม่เพียง แต่คุณแก้ไขinstanceofปัญหา แต่คุณยังเก็บข้อผิดพลาดปกติconsole.log:

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

11

นี่คือวิธีที่คุณสามารถสร้างข้อผิดพลาดที่กำหนดเองกับสมบูรณ์เหมือนกับพื้นเมืองErrorพฤติกรรม เทคนิคนี้ใช้ได้เฉพาะใน Chrome และ node.jsเท่านั้น ฉันจะไม่แนะนำให้ใช้ถ้าคุณไม่เข้าใจว่ามันทำอะไร

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;
    }

})();

ในฐานะที่เป็น reasult เราได้รับ

/**
 * 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)

จากนั้นคุณสามารถใช้สิ่งนี้:

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

และใช้NotImplementedErrorตามที่คุณต้องการError:

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

และมันจะทำงานตามที่คาดไว้:

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!

โปรดทราบว่าใช้error.stackงานได้อย่างถูกต้องและไม่รวมNotImplementedErrorการโทรคอนสตรัค (ขอบคุณ v8 ของError.captureStackTrace())

บันทึก. eval()นอกจากนี้ที่น่าเกลียด err.constructor.nameเหตุผลเดียวที่มันถูกนำมาใช้คือการได้รับที่ถูกต้อง หากคุณไม่ต้องการคุณสามารถทำให้ทุกอย่างง่ายขึ้นเล็กน้อย


2
Error.apply(self, arguments)จะระบุไม่ได้ไปทำงาน ฉันขอแนะนำให้คัดลอกการติดตามสแต็กแทนซึ่งเข้ากันได้ข้ามเบราว์เซอร์
Kornel

11

ฉันมักจะใช้วิธีการที่มีมรดกต้นแบบ การเอาชนะtoString()จะช่วยให้คุณได้เปรียบที่เครื่องมือเช่น Firebug จะบันทึกข้อมูลจริงแทนที่จะ[object Object]ไปยังคอนโซลสำหรับข้อยกเว้นที่ไม่ได้ตรวจสอบ

ใช้instanceofเพื่อกำหนดประเภทของข้อยกเว้น

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();

8

ES6

ด้วยคลาสใหม่และขยายคำหลักตอนนี้ง่ายขึ้นมาก:

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

7

ใช้คำสั่งThrow

JavaScript ไม่สนใจประเภทของข้อยกเว้น (เช่น Java) JavaScript เพิ่งสังเกตเห็นมีข้อยกเว้นและเมื่อคุณจับคุณสามารถ "ดู" สิ่งที่ยกเว้น "พูดว่า"

หากคุณมีประเภทข้อยกเว้นต่าง ๆ ที่คุณต้องโยนฉันขอแนะนำให้ใช้ตัวแปรที่มีสตริง / วัตถุของข้อยกเว้นเช่นข้อความ ที่ใดที่คุณต้องการให้ใช้ "throw myException" และใน catch ให้เปรียบเทียบข้อยกเว้น catch กับ myException


1

ดูตัวอย่างนี้ใน MDN

หากคุณต้องการกำหนดข้อผิดพลาดหลายรายการ (ทดสอบรหัสที่นี่ !):

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;

รหัสส่วนใหญ่จะถูกคัดลอกมาจาก: วิธีที่ดีในการขยายข้อผิดพลาดใน JavaScript คืออะไร?


1

ทางเลือกสำหรับคำตอบของasselinสำหรับใช้กับคลาส ES2015

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}

1
//create error object
var error = new Object();
error.reason="some reason!";

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

ตอนนี้เราตั้งค่าเพิ่มเหตุผลหรือคุณสมบัติใด ๆ ที่เราต้องการวัตถุข้อผิดพลาดและดึงมัน โดยทำให้เกิดข้อผิดพลาดที่สมเหตุสมผลมากขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.