Loại ngoại lệ tùy chỉnh


224

Tôi có thể xác định loại tùy chỉnh cho các ngoại lệ do người dùng xác định trong JavaScript không? Nếu vậy, tôi sẽ làm thế nào?


3
Coi chừng. Theo JavaScript trong 10 phút, bạn sẽ không nhận được dấu vết ngăn xếp nếu bạn ném một giá trị không được hộp.
Janus Troelsen

exceptionsjs.com cung cấp khả năng tạo các ngoại lệ tùy chỉnh và cung cấp một số ngoại lệ bị thiếu bao gồm ArgumentException và NotImcellenceed theo mặc định.
Steven Wexler

Câu trả lời:


232

Từ 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 Đó là trong "JavaScript: Các bộ phận tốt" (cuốn sách tuyệt vời IMO). Đây Sách của Google xem trước chương trình phần: books.google.com/books?id=PXa2bby0oQ0C&pg=PA32&lpg=PA32
orip

11
Thêm một phương thức toString sẽ làm cho nó hiển thị độc đáo trong bảng điều khiển javascript. không có nó hiển thị như: Uncaught # <Object> với toString, nó hiển thị như: Uncaught System Error: Lỗi được phát hiện. Hãy liên hệ với quản trị hệ thống.
JDC

11
Điều này sẽ không cho phép bạn xếp chồng dấu vết trừ khi bạn thừa hưởng từ Lỗi
Luke H

Làm thế nào bạn có thể lọc trong một khối bắt để chỉ hoạt động với lỗi tùy chỉnh này?
Overdrivr

@overdrivr đại loại như catch (e) { if (e instanceof TypeError) { … } else { throw e; } }⦃⦄ hoặc catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }⦃⦄.
sam boosalis

92

Bạn nên tạo một ngoại lệ tùy chỉnh mà nguyên mẫu kế thừa từ Lỗi. Ví dụ:

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;

Về cơ bản, đây là phiên bản đơn giản hóa của những gì bị biến dạng được đăng ở trên với sự cải tiến mà dấu vết ngăn xếp hoạt động trên Firefox và các trình duyệt khác. Nó đáp ứng các bài kiểm tra tương tự mà ông đã đăng:

Sử dụng:

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

Và nó sẽ hành xử được mong đợi:

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

Bạn có thể thực hiện các ngoại lệ của riêng bạn và xử lý chúng chẳng hạn như ở đây:

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

Có một cú pháp khác để bắt ngoại lệ được gõ, mặc dù điều này sẽ không hoạt động trong mọi trình duyệt (ví dụ không phải trong 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
Trang web MSN đưa ra cảnh báo này về các điều kiện bắt: Không chuẩn Tính năng này không chuẩn và không theo dõi tiêu chuẩn. Không sử dụng nó trên các trang web sản xuất phải đối mặt với Web: nó sẽ không hoạt động cho mọi người dùng. Cũng có thể có sự không tương thích lớn giữa việc thực hiện và hành vi có thể thay đổi trong tương lai.
Lawrence Dol

40

Đúng. Bạn có thể ném bất cứ thứ gì bạn muốn: số nguyên, chuỗi, đối tượng, bất cứ thứ gì. Nếu bạn muốn ném một đối tượng, thì chỉ cần tạo một đối tượng mới, giống như bạn sẽ tạo một đối tượng trong các trường hợp khác, và sau đó ném nó. Tài liệu tham khảo Javascript của Mozilla có một số ví dụ.


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

MyError.prototype = new Error;

Điều này cho phép sử dụng như ..

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

Ví dụ ngắn gọn này có hoạt động chính xác như vậy ngay cả khi bạn không kế thừa nguyên mẫu của Error không? Nó không rõ ràng với tôi những gì mang lại cho bạn trong ví dụ này.
EleventyOne

1
Không, e instanceof Errorsẽ là sai.
Morgan ARR Allen

Thật. Nhưng vì e instanceof MyErrorsẽ là sự thật, else if(e instanceof Error)tuyên bố sẽ không bao giờ được đánh giá.
EleventyOne

Đúng vậy, đây chỉ là một ví dụ về cách hoạt động của kiểu thử / bắt này. Nơi else if(e instanceof Error)sẽ là bắt cuối cùng. Có khả năng theo sau là một đơn giản else(mà tôi không bao gồm). Sắp xếp giống như default:trong một câu lệnh chuyển đổi nhưng cho lỗi.
Morgan ARR Allen

15

Nói ngắn gọn:

  • Nếu bạn đang sử dụng ES6 mà không có bộ chuyển đổi :

    class CustomError extends Error { /* ... */}

    Xem Lỗi mở rộng trong Javascript với cú pháp ES6 để biết cách thực hành tốt nhất hiện tại

  • Nếu bạn đang sử dụng bộ chuyển mã Babel :

Tùy chọn 1: sử dụng babel-plugin-Transform-buildin-extend

Tùy chọn 2: tự làm (lấy cảm hứng từ cùng một thư viện)

    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);
  • Nếu bạn đang sử dụng ES5 thuần túy :

    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;
    }
  • Thay thế: sử dụng khung Classtrophobic

Giải trình:

Tại sao việc mở rộng lớp Error bằng ES6 và Babel là một vấn đề?

Bởi vì một phiên bản của CustomError không còn được công nhận như vậy nữa.

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

Trong thực tế, từ các tài liệu chính thức của Babel, bạn không thể mở rộng bất kỳ được xây dựng trong các lớp học JavaScript như Date, Array, DOMhoặc Error.

Vấn đề được mô tả ở đây:

Những câu trả lời SO khác thì sao?

Tất cả các câu trả lời đã khắc phục sự cố instanceofnhưng bạn mất lỗi thông thường console.log:

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

Trong khi sử dụng phương pháp được đề cập ở trên, không chỉ bạn khắc phục sự cố instanceofmà bạn còn giữ lỗi thường xuyên console.log:

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

11

Đây là cách bạn có thể tạo các lỗi tùy chỉnh hoàn toàn giống với Errorhành vi của người bản xứ . Kỹ thuật này chỉ hoạt động trong Chrome và Node.js cho bây giờ. Tôi cũng không khuyên bạn nên sử dụng nó nếu bạn không hiểu nó làm gì.

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

})();

Như một reasult chúng tôi nhận được

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

Sau đó, bạn có thể sử dụng nó như thế này:

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

Và sử dụng NotImplementedErrornhư bạn muốn Error:

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

Và nó sẽ hành xử được mong đợi:

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!

Lưu ý, điều đó error.stackhoạt động hoàn toàn chính xác và sẽ không bao gồm NotImplementedErrorcuộc gọi của nhà xây dựng (nhờ v8's Error.captureStackTrace()).

Ghi chú. Có xấu xí eval(). Lý do duy nhất nó được sử dụng là để có được chính xác err.constructor.name. Nếu bạn không cần nó, bạn có thể đơn giản hóa mọi thứ.


2
Error.apply(self, arguments)được chỉ định không hoạt động . Tôi đề nghị sao chép theo dõi ngăn xếp thay vì tương thích trình duyệt chéo.
Kornel

11

Tôi thường sử dụng một cách tiếp cận với thừa kế nguyên mẫu. Ghi đè toString()cung cấp cho bạn lợi thế là các công cụ như Fireorms sẽ ghi lại thông tin thực tế thay vì [object Object]vào bảng điều khiển cho các trường hợp ngoại lệ chưa được phát hiện.

Sử dụng instanceofđể xác định loại ngoại lệ.

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

Sao chépIdException.js

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

ns.DuplicateIdException.prototype = new ns.Exception();

8

ES6

Với lớp mới và mở rộng từ khóa giờ đây dễ dàng hơn nhiều:

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

7

Sử dụng câu lệnh ném .

JavaScript không quan tâm loại ngoại lệ là gì (như Java làm). JavaScript chỉ cần thông báo, có một ngoại lệ và khi bạn bắt được nó, bạn có thể "xem" ngoại lệ "nói gì".

Nếu bạn có các loại ngoại lệ khác nhau mà bạn phải ném, tôi khuyên bạn nên sử dụng các biến có chứa chuỗi / đối tượng của ngoại lệ tức là thông báo. Trường hợp bạn cần sử dụng "throw myException" và trong phần bắt, hãy so sánh ngoại lệ bị bắt với myException.


1

Xem ví dụ này trong MDN.

Nếu bạn cần xác định nhiều lỗi (kiểm tra mã ở đây !):

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;

Mã được sao chép chủ yếu từ: Cách tốt để mở rộng Lỗi trong JavaScript là gì?


1

Một thay thế cho câu trả lời của asselin để sử dụng với các lớp 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;
    }
}

Bây giờ chúng ta đặt thêm lý do hoặc bất kỳ thuộc tính nào chúng ta muốn vào đối tượng lỗi và truy xuất nó. Bằng cách làm cho lỗi hợp lý hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.