JavaScript에서 오류를 확장하는 좋은 방법은 무엇입니까?


368

내 JS 코드에 몇 가지를 던져보고 오류의 인스턴스가되기를 원하지만 다른 것으로 만들고 싶습니다.

파이썬에서는 일반적으로 Exception을 서브 클래스 화합니다.

JS에서해야 할 일은 무엇입니까?

답변:


217

Error 필드에있는 유일한 표준 필드는 message속성입니다. ( MDN 또는 EcmaScript 언어 사양, 섹션 15.11 참조) 기타 모든 것은 플랫폼에 따라 다릅니다.

대부분의 사람들의 환경을 설정 stack, 속성을하지만 fileName하고lineNumber 상속에 사용되는 거의 쓸모가 없습니다.

따라서 최소한의 접근 방식은 다음과 같습니다.

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

스택을 스니핑하고, 원치 않는 요소를 시프트 해제하고, fileName 및 lineNumber와 같은 정보를 추출 할 수 있지만, 현재 실행중인 플랫폼 JavaScript에 대한 정보가 필요합니다. 대부분의 경우는 불필요하며 실제로 원한다면 사후에 할 수 있습니다.

사파리 는 주목할만한 예외입니다. stack속성 은 없지만 던져지는 객체 의 throw키워드 세트 sourceURLline속성이 있습니다. 그것들은 정확하다고 보장됩니다.

내가 사용한 테스트 사례는 다음에서 찾을 수 있습니다. JavaScript 자체 제작 오류 객체 비교 .


19
this.name = 'MyError' 함수 외부로 이동하여로 변경할 수 MyError.prototype.name = 'MyError'있습니다.
Daniel Beardsley

36
이것은 스타일의 문제로 아마도 이것처럼 쓸 것입니다. function MyError(message) { this.message = message; this.stack = Error().stack; } MyError.prototype = Object.create(Error.prototype); MyError.prototype.name = "MyError";
kybernetikos

11
나도 추가 할 것 MyError.prototype.constructor = MyError입니다.
Bharat Khatri

3
ES6에서 Error.call (this, message); 초기화해야 this합니까?
4esn0k

4
MyError.prototype = Object.create (Error.prototype);
로빈 새처럼

170

ES6에서 :

class MyError extends Error {
  constructor(message) {
    super(message);
    this.name = 'MyError';
  }
}

출처


53
하위 클래스가 클래스를 확장해야하므로 Babel과 같은 변환기를 통해 ES6 기능을 사용하는 경우에는 작동하지 않습니다.
aaaidan

6
babel을 사용하고 노드> 5.x에있는 경우 es2015 사전 설정을 사용하지 않아야하지만 npmjs.com/package/babel-preset-node5 를 사용하면 기본 es6 확장 및 그 이상을 사용할 수 있습니다.
Ace

2
가능하면이를 수행하는 가장 좋은 방법입니다. 맞춤 오류는 Chrome 및 Firefox (및 아마도 다른 브라우저)의 일반 오류와 비슷합니다.
Matt Browne

2
브라우저의 경우 런타임시 클래스 지원을 감지하고 그에 따라 비 클래스 버전으로 대체 할 수 있습니다. 탐지 코드 :var supportsClasses = false; try {eval('class X{}'); supportsClasses = true;} catch (e) {}
Matt Browne

31
유지 관리를 쉽게 하려면 this.name = this.constructor.name;대신 사용하십시오.
Константин Ван

52

한마디로 :

  • 트랜스 파일러없이 ES6 사용하는 경우 :

    class CustomError extends Error { /* ... */}
  • Babel 트랜스 파일러를 사용하는 경우 :

옵션 1 : babel-plugin-transform-builtin-extend 사용

옵션 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) {
      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;
    }
  • 대안 : Classtrophobic framework 사용

설명:

ES6 및 Babel을 사용하여 Error 클래스를 확장하는 것이 왜 문제입니까?

CustomError 인스턴스는 더 이상 인식되지 않기 때문입니다.

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

사실, 바벨의 공식 문서에서, 당신은 어떤 내장 된 자바 스크립트 클래스를 확장 할 수 없습니다 와 같은 Date, Array, DOM또는 Error.

문제는 여기에 설명되어 있습니다.

다른 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

1
class CustomError extends Error { /* ... */}공급 업체별 인수 ( lineNumber, 등)를 올바르게 처리하지 않습니다 . 'ES6 구문을 사용하는 Javascript의 확장 오류'는 Babel에 따라 다르며 ES5 솔루션이 사용 const하며 사용자 지정 인수를 처리하지 않습니다.
Indolering

매우 완전한 답변.
Daniele Orlando

이것은 실제로 가장 포괄적 인 솔루션을 제공하고 다양한 부분이 필요한 이유를 설명합니다. JBE 감사합니다!
AndrewSouthpaw

이를 통해 "오류"에서 상속하는 문제를 해결하는 데 도움이되었습니다. 2 시간의 악몽이었다!
Iulian Pinzaru

2
console.log(new CustomError('test') instanceof CustomError);// false글을 쓰는 시점 에는 문제가 있었지만 현재 해결되었다는 점에 주목할 가치가 있습니다. 실제로 답변에 연결된 문제 가 해결되었으며 여기 에서 REPL에 코드를 붙여넣고 올바른 프로토 타입 체인으로 인스턴스화하기 위해 코드 가 올바르게 변환되는 방법을 확인하여 올바른 동작을 테스트 할 수 있습니다 .
Nobita

44

편집 : 의견을 읽으십시오. 이것은 V8 (Chrome / Node.JS)에서만 잘 작동합니다. 내 의도는 모든 브라우저에서 작동하는 브라우저 간 솔루션을 제공하고 지원이있는 곳에서 스택 추적을 제공하는 것이 었습니다.

편집 : 더 많은 편집을 할 수 있도록이 커뮤니티 위키를 만들었습니다.

V8 용 솔루션 (Chrome / Node.JS)은 Firefox에서 작동하며 IE에서 대부분 올바르게 작동하도록 수정할 수 있습니다. (포스트 끝 참조)

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
}

"코드를 보여줘!"

짧은 버전 :

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype
  Error.captureStackTrace(this, this.constructor)
  this.name = this.constructor.name
  this.message = message
}

나는 계속 this.constructor.prototype.__proto__ = Error.prototype함께 모든 코드를 유지하기 위해 함수 내에서. 그러나 당신은 또한 대체 할 수있는 this.constructorUserError그는 한 번만 호출되는 있도록 함수 외부에 코드를 이동할 수 있습니다.

해당 경로로 이동 하면 처음 던지기 전에 해당 회선에 전화해야합니다UserError .

순서에 관계없이 함수가 먼저 작성되므로주의해야 할 점은 함수에 적용되지 않습니다. 따라서 문제없이 파일 끝으로 함수를 이동할 수 있습니다.

브라우저 호환성

Firefox 및 Chrome (및 Node.JS)에서 작동하며 모든 약속을 채 웁니다.

다음에서 Internet Explorer가 실패합니다

  • 오류는 err.stack"내 잘못이 아니다"로 시작할 필요가 없습니다 .

  • Error.captureStackTrace(this, this.constructor) 존재하지 않으므로 다른 것을해야합니다.

    if(Error.captureStackTrace) // AKA if not IE
        Error.captureStackTrace(this, this.constructor)
  • toString서브 클래 싱 할 때 존재하지 않습니다 Error. 따라서 추가해야합니다.

    else
        this.toString = function () { return this.name + ': ' + this.message }
  • IE는 고려하지 않을 것이다 UserError을 할 instanceof Error당신은 당신이 전에 약간의 시간 다음 실행하지 않으면throw UserError

    UserError.prototype = Error.prototype

16
Firefox에 실제로 captureStackTrace가 있다고 생각하지 않습니다. V8 확장이며 Firefox에서는 정의되어 있지 않으며 웹에서 Firefox를 지원하는 참조를 찾을 수 없습니다. (고마워!)
Geoff

5
Error.call(this)실제로 수정하지 않고 오류를 반환 하기 때문에 아무것도하지 않습니다 this.
kybernetikos

1
Node.js를 위해 훌륭하게 작동합니다
Rudolf Meijering

1
UserError.prototype = Error.prototype오해의 소지가 있습니다. 이것은 상속을하지 않습니다. 이것은 그것들 을 같은 클래스로 만듭니다.
Halcyon

1
나는 적어도 현재 브라우저에서 Object.setPrototypeOf(this.constructor.prototype, Error.prototype)선호하는 것으로 생각 this.constructor.prototype.__proto__ = Error.prototype합니다.
ChrisV

29

모든 다른 유형의 오류에 대한 상용구피하기 위해 일부 솔루션의 지혜를 createErrorType함수 로 결합했습니다  .

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

그런 다음 다음과 같이 새 오류 유형을 쉽게 정의 할 수 있습니다 .

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;있습니까?
Peter Tseng

@PeterTseng name프로토 타입에 이미 설정되어 있으므로 더 이상 필요하지 않습니다. 나는 그것을 제거했다. 감사!
Ruben Verborgh

27

에서 2018 , 나는 이것이 가장 좋은 방법이라고 생각; IE9 + 및 최신 브라우저를 지원합니다.

업데이트 : 다른 구현에 대한 비교는 이 테스트저장소 를 참조하십시오 .

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

또한 다른 답변에 널리 사용되는 __proto__속성은 더 이상 사용되지 않습니다 .


1
왜 사용하고 setPrototypeOf()있습니까? 적어도 MDN에 따르면 .prototype생성자 에서 속성을 설정하여 동일한 작업을 수행 할 수있는 경우 (사용 else하지 않는 탐색 블록 에서 수행하는 것처럼) 동일한 작업을 수행 할 수 있다면 일반적으로 사용 하지 않는 것이 setPrototypeOf좋습니다.
매트 브라운

객체의 프로토 타입을 변경하는 것은하지 말고 모두 함께 권장하지 않습니다 setPrototypeOf. 그러나 OP가 요구하는대로 여전히 필요한 경우 기본 제공 방법을 사용해야합니다. MDN에서 알 수 있듯이 이것은 객체의 프로토 타입을 설정하는 올바른 방법으로 간주됩니다. 다시 말해 MDN은 프로토 타입을 변경하지 않고 (성능 및 최적화에 영향을 미치기 때문에)을 사용해야한다고 말합니다 setPrototypeOf.
오 누르 일림

내 요점은 실제로 프로토 타입을 변경할 필요가 없다고 생각했습니다. 하단 ( CustomError.prototype = Object.create(Error.prototype)) 에서 간단하게 라인을 사용할 수 있습니다 . 또한의 Object.setPrototypeOf(CustomError, Error.prototype)새 인스턴스에 대한 프로토 타입을 지정하지 않고 생성자 자체의 프로토 타입을 설정합니다 CustomError. 어쨌든, 2016 년에는 실제로 오류를 확장하는 더 좋은 방법이 있다고 생각하지만 여전히 Babel과 함께 사용하는 방법을 알아 내고 있습니다 : github.com/loganfsmyth/babel-plugin-transform-builtin-extend/…
Matt Browne

CustomError.prototype = Object.create(Error.prototype)프로토 타입도 바꾸고 있습니다. ES5에는 내장 확장 / 상속 로직이 없으므로 변경해야합니다. 나는 당신이 언급 한 babel 플러그인이 비슷한 일을한다고 확신합니다.
Onur Yıldırım

1
gist.github.com/mbrowne/4af54767dcb3d529648f5a8aa11d6348과 같이Object.setPrototypeOf 사용이 의미가없는 이유를 설명하는 요점을 만들었습니다 . 아마도 당신은 글을 쓰는 것을 의미 했을 것입니다-약간 더 의미가 있습니다 (단순히 설정하는 것보다 이점을 제공하지는 않지만 ). Object.setPrototypeOf(CustomError.prototype, Error.prototype)CustomError.prototype
Matt Browne

19

완벽을 기하기 위해-이전의 답변 중 어느 것도이 방법을 언급하지 않았기 때문에-Node.js로 작업하고 브라우저 호환성에 신경 쓸 필요가 없다면 내장 된 기능으로 원하는 효과를 얻는 것이 매우 쉽습니다. inheritsutil모듈 ( 공식 문서 여기 ).

예를 들어, 오류 코드를 첫 번째 인수로 사용하고 오류 메시지를 두 번째 인수로 사용하는 사용자 정의 오류 클래스를 작성한다고 가정하십시오.

custom-error.js 파일 :

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

이제 당신은 인스턴스화하고 전달 / 던질 수 있습니다 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!');

이 스 니펫을 사용하면 스택 추적에 올바른 파일 이름과 행이 있으며 오류 인스턴스의 이름이 올바른 것입니다!

이는 대상 객체에 속성 captureStackTrace을 생성하는 메소드 사용 stack(이 경우 CustomError인스턴스화 됨) 으로 인해 발생합니다 . 작동 방식에 대한 자세한 내용은 여기 에서 설명서를 확인 하십시오 .


1
this.message = this.message;이것이 잘못되었거나 JS에 대해 모르는 미친 것들이 있습니까?
Alex

1
안녕하세요 @Alex, 당신은 완전히 옳습니다! 이제 수정되었습니다. 감사!
Victor Schröder

18

Crescent Fresh의 답변은 투표율이 높습니다. 그의 경고는 유효하지 않지만 그가 다루지 않은 다른 제한이 있습니다.

첫째, 초승달의 "주의 사항"단락의 추론은 의미가 없습니다. 이 설명은 여러 개의 catch 문과 비교하여 "if if (Error instance of MyError) else ..."코딩이 다소 부담 스럽거나 장황하다는 것을 의미합니다. 단일 catch 블록에있는 여러 개의 instanceof 문은 여러 catch 문과 마찬가지로 간결합니다. 트릭없이 깨끗하고 간결한 코드입니다. 이것은 Java의 훌륭한 던지기 가능한 하위 유형별 오류 처리를 에뮬레이트하는 좋은 방법입니다.

WRT는 "서브 클래스의 메시지 특성이 설정되지 않은 것으로 나타납니다."는 올바르게 구성된 오류 서브 클래스를 사용하는 경우에는 해당되지 않습니다. 자신 만의 ErrorX Error 서브 클래스를 만들려면 "var MyError ="로 시작하는 코드 블록을 복사하여 "MyError"라는 단어를 "ErrorX"로 변경하십시오. 서브 클래스에 사용자 정의 메소드를 추가하려면 샘플 텍스트를 따르십시오.

JavaScript 오류 서브 클래 싱의 실질적이고 중요한 제한은 FireFox와 같이 스택 추적 및 인스턴스 위치를 추적하고보고하는 JavaScript 구현 또는 디버거의 경우 자체 오류 서브 클래스 구현의 위치가 인스턴스화 지점으로 기록된다는 것입니다 직접 오류를 사용한 경우 "new Error (...)"를 실행 한 위치가됩니다. IE 사용자는 아마 눈치 채지 못했지만 FF의 Fire Bug 사용자는 이러한 오류와 함께보고 된 쓸모없는 파일 이름 및 줄 번호 값을 보게되며 실제 인스턴스화 위치를 찾으려면 스택 추적을 요소 # 1로 드릴 다운해야합니다.


내가 올바르게 얻었습니까? 하위 클래스를 서브 클래스 화하지 않고 새로운 Error (...)를 직접 사용하면 파일 이름과 줄이 올바르게보고되고 있습니까? 그리고 기본적으로 실제 (섹시하거나 장식적인 것이 아닌) 서브 클래 싱 오류에서 의미가 없다고 말합니까?
jayarjo

6
이 답변은 Crescent Fresh's삭제 된 혼란스러운 것입니다 !
Peter

이것은 여전히 ​​그렇습니까? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 줄 번호는 2가 아닌 곳입니다
Muhammad Umer

13

이 솔루션은 어떻습니까?

다음을 사용하여 사용자 정의 오류를 발생시키는 대신 :

throw new MyError("Oops!");

Error 객체 (데코레이터와 같은 종류)를 래핑합니다.

throw new MyError(Error("Oops!"));

스택, fileName lineNumber 등과 같은 모든 속성이 올바른지 확인합니다.

그런 다음 속성을 복사하거나 속성을 정의하기 만하면됩니다. 다음은 게터 (IE9)를 사용하는 예입니다.

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

1
이 솔루션을 npm 패키지로 출시했습니다 : npmjs.com/package/throwable
JoWie

엄청나게 우아한 솔루션, 공유해 주셔서 감사합니다! 하나의 변형 : new MyErr (arg1, arg2, new Error())그리고 MyErr 생성자에서 우리 Object.assign는 마지막 arg의 속성을 할당하는데 사용 합니다this
Peeyush Kushwaha

나는 이것을 좋아한다. 상속 대신 캡슐화를 사용하여 제한을 무시합니다.
Jenny O'Reilly

13

일부 사람들이 말했듯이 ES6에서는 상당히 쉽습니다.

class CustomError extends Error { }

그래서 내 앱 (Angular, Typescript)에서 시도했지만 작동하지 않았습니다. 얼마 후 Typescript에서 문제가 발생한다는 것을 알았습니다.

https://github.com/Microsoft/TypeScript/issues/13965를 참조 하십시오.

그렇게하면 매우 혼란 스럽습니다.

class CustomError extends Error {}


try {
  throw new CustomError()
} catch(e) {
  if (e instanceof CustomError) {
    console.log('Custom error');
  } else {
    console.log('Basic error');
  }
}

노드 또는 브라우저에 직접 표시됩니다. Custom error

Typescript 놀이터의 프로젝트에서 Typescript로 실행하십시오 Basic error.

해결책은 다음을 수행하는 것입니다.

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

10

내 솔루션은 제공된 다른 답변보다 간단하며 단점이 없습니다.

특정 프로토 타입 지식이 없어도 Error 프로토 타입 체인과 Error의 모든 속성을 보존합니다. Chrome, Firefox, Node 및 IE11에서 테스트되었습니다.

유일한 제한은 호출 스택 맨 위에있는 추가 항목입니다. 그러나 그것은 쉽게 무시됩니다.

다음은 두 가지 맞춤 매개 변수가 포함 된 예입니다.

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

사용법 예 :

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의 폴리 파일이 필요한 환경의 경우 :

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
};

1
내 대답에 설명되어 있습니다,이 솔루션은 콘솔에서 스택 추적의 첫 줄을 로그 파이어 폭스 등 브라우저에 문제가 발생할 수 있습니다
조나단 벤

이것은 ES5에서 잘 작동하는 유일한 답변입니다 (ES6 클래스 사용도 잘 작동합니다). Chromium DevTools의 오류는 다른 답변보다 훨씬 잘 표시됩니다.
벤 구 블러

참고 : TypeScript와 함께이 솔루션을 사용하는 경우 throw CustomError('err')대신 다음을 실행해야합니다.throw new CustomError('err')
Ben Gubler

8

위의 예에서 Error.apply(또한 Error.call)는 나를 위해 아무것도하지 않습니다 (Firefox 3.6 / Chrome 5). 내가 사용하는 해결 방법은 다음과 같습니다.

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

5

다른 사람들이 말했듯이 Node에서는 간단합니다.

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

3

다른 사람들이 이미 말한 내용에 추가하고 싶습니다.

사용자 정의 오류 클래스가 스택 추적에 올바르게 표시되도록하려면 사용자 정의 오류 클래스의 프로토 타입 이름 속성을 사용자 정의 오류 클래스의 이름 속성으로 설정해야합니다. 이것이 내가 의미하는 바입니다.

CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';

전체 예는 다음과 같습니다.

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

모든 것이 말되고 완료되면 새로운 예외를 던지고 다음과 같이 보입니다 (크롬 개발 도구에서 느리게 시도했습니다).

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)

5
모든 오류 인스턴스 의 이름 속성을 덮어 쓰지 않습니까?
panzi

@ panzi 당신이 맞습니다. 작은 버그를 수정했습니다. 고마워요!
Gautham C.

3

내 2 센트 :

왜 또 다른 대답?

a) Error.stack속성에 액세스하면 (일부 답변에서와 같이) 성능이 저하 될 수 있습니다.

b) 한 줄이기 때문에.

c) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error 의 솔루션이 스택 정보를 보존하지 않는 것 같습니다.

//MyError class constructor
function MyError(msg){
    this.__proto__.__proto__ = Error.apply(null, arguments);
};

사용 예

http://jsfiddle.net/luciotato/xXyeB/

무엇을합니까?

this.__proto__.__proto__입니다 MyError.prototype.__proto__. 따라서 __proto__MyError 의 FOR ALL INSTANCES를 새로 생성 된 특정 Error로 설정합니다. MyError 클래스 속성과 메서드를 유지하고 새 Error 속성 (.stack 포함)을 __proto__체인에 넣습니다 .

명백한 문제 :

유용한 스택 정보가있는 MyError 인스턴스를 두 개 이상 가질 수 없습니다.

무엇을 완전히 이해하지 못하면이 솔루션을 사용하지 마십시오 this.__proto__.__proto__=.


2

JavaScript 예외는 서브 클래스하기가 어렵 기 때문에 서브 클래스가 아닙니다. 방금 새 Exception 클래스를 만들고 그 안에 Error를 사용합니다. 콘솔의 사용자 정의 예외처럼 보이도록 Error.name 속성을 변경합니다.

var InvalidInputError = function(message) {
    var error = new Error(message);
    error.name = 'InvalidInputError';
    return error;
};

위의 새로운 예외는 일반 오류처럼 발생할 수 있으며 예를 들어 다음과 같이 작동합니다.

throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string 

주의 사항 : 스택 추적은 새 오류가 발생하는 곳과 던지는 곳이 아니라 완벽하지 않습니다. Chrome에서는 콘솔에서 직접 전체 스택 추적을 제공하므로 Chrome에서 큰 문제가 아닙니다. 그러나 Firefox에서는 더 문제가 있습니다.


이 경우에는 실패m = new InvalidInputError(); dontThrowMeYet(m);
Eric

@ 에릭 나는 동의하지만 이것은 꽤 작은 제한처럼 보입니다. 예외 객체를 미리 인스턴스화 할 필요가 없었습니다 (위의 코드 샘플과 같은 메타 프로그래밍 사용 제외). 이것이 정말로 당신에게 문제입니까?
Jonathan Benn

예, 행동이 동일 해 보이므로 답변을 변경하겠습니다. 아니에요 100 % 파이어 폭스와 크롬의 "VAR 오류"라인을 제공 스택 추적, 만족
조나단 벤

1
@JonathanBenn 나는 파티에 늦었으므로 이미 집어 들었을 것입니다. 비동기 프로그래밍 및 약속을 사용할 때 예외 개체를 자주 인스턴스화합니다. @Eric의 이름에 따라 m = new ...then을 자주 사용 Promise.reject(m)합니다. 꼭 필요한 것은 아니지만 코드를 쉽게 읽을 수 있습니다.
BaldEagle

1
@JonathanBenn : (그는) 10 월 14 일 예외 객체를 던지기 전에 인스턴스화하는 것이 드문 것이라고 생각하는 것 같습니다. 나는 한 번의 예를 들었다. 나는 그것이 일반적이라고 말하지는 않지만 원할 때 가지고 있으면 편리합니다. 그리고 인스턴스화는 모두 한 줄에 있고 거부는 모두 다른 줄에 있기 때문에 내 코드는 더 읽기 쉽습니다. 나는 그것을 희망합니다!
BaldEagle

2

Mohsen의 답변에서 지적했듯이 ES6에서는 클래스를 사용하여 오류를 확장 할 수 있습니다. 훨씬 더 쉽고 동작이 기본 오류와 더 일관성이 있지만 불행히도 ES6 이전 브라우저를 지원 해야하는 경우 브라우저에서이를 사용하는 것은 간단하지 않습니다. 구현 방법에 대한 참고 사항은 아래를 참조하십시오. 그러나 그 동안 다른 답변의 최상의 제안 중 일부를 포함하는 비교적 간단한 접근법을 제안합니다.

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에서는 다음과 같이 간단합니다.

class CustomError extends Error {}

...을 사용하여 ES6 클래스에 대한 지원을 감지 할 수 try {eval('class X{}')있지만 이전 브라우저에서로드 한 스크립트에 ES6 버전을 포함하려고하면 구문 오류가 발생합니다. 따라서 모든 브라우저를 지원하는 유일한 방법은 eval()ES6를 지원하는 브라우저에 대해 별도의 스크립트를 동적으로로드하는 것입니다 (예 : AJAX 또는 ). 더 복잡한 것은eval() 모든 환경 (콘텐츠 보안 정책으로 인해)에서 지원되지 않는데, 이는 프로젝트에서 고려하거나 고려하지 않을 수 있습니다.

지금까지는 위의 첫 번째 접근법이나 단순히 Error 확장하지 않고 직접 하는 것이 비 ES6 브라우저를 지원 해야하는 코드에서 실제로 수행 할 수있는 최선의 방법 인 것 같습니다.

일부 사람들이 고려해야 할 또 다른 접근법이 Object.setPrototypeOf()있습니다. 사용자 정의 오류 유형의 인스턴스이지만 콘솔의 기본 오류처럼 보이고 동작하는 오류 객체를 만드는 데 사용할 수 있는 곳 을 사용 하는 것입니다 ( Ben의 답변 덕분에) 추천을 위해). https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8에 대한 접근 방식은 다음과 같습니다 . 그러나 언젠가 우리는 ES6 만 사용할 수 있다는 것을 감안할 때 개인적으로 그 접근 방식의 복잡성이 가치가 있는지 확신하지 못합니다.


1

이를 올바르게 수행하는 방법은 생성자로부터 적용 결과를 반환하고 일반적인 복잡한 자바 스크립트 방식으로 프로토 타입을 설정하는 것입니다.

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 ...>

이 시점 에서이 작업을 수행하는 방법에 대한 유일한 문제는 (반복했습니다)

  • stackmessage에 포함되지 않은 속성 MyError
  • stacktrace에는 실제로 필요하지 않은 추가 줄이 있습니다.

첫 번째 문제는이 답변의 트릭을 사용하여 열거 할 수없는 모든 오류 속성을 반복하여 해결할 수 있습니다. . 객체의 열거 할 수없는 상속 된 속성 이름을 가져올 수 있습니까? , 그러나 이것은 <9에서 지원되지 않습니다. 두 번째 문제는 스택 추적에서 해당 줄을 찢어 해결할 수는 있지만 안전하게 수행하는 방법을 모르겠습니다 (e.stack.toString () ??의 두 번째 줄을 제거하는 것일 수 있습니다 ??).


오류를 포함하여 가장 일반적인 오래된 자바 스크립트 객체를 확장 할 수있는 모듈을 만들었습니다. 이 시점에서 꽤 성숙합니다. github.com/fresheneesz/proto
BT

1

스 니펫이 모든 것을 보여줍니다.

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

0

한 걸음 물러서서 왜 그렇게하고 싶을까? 요점은 다른 오류를 다르게 처리하는 것입니다.

예를 들어, 파이썬에서는 catch 문만 catch로 제한 할 수 MyValidationError있으며, 자바 스크립트에서 비슷한 작업을 수행하려고 할 수 있습니다.

catch (MyValidationError e) {
    ....
}

자바 스크립트에서는이 작업을 수행 할 수 없습니다. 캐치 블록은 하나만있을 것입니다. 오류에 if 문을 사용하여 유형을 결정해야합니다.

catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }

대신 유형, 메시지 및 기타 적합한 속성을 가진 원시 객체를 던질 것이라고 생각합니다.

throw { type: "validation", message: "Invalid timestamp" }

그리고 당신이 오류를 잡을 때 :

catch(e) {
    if(e.type === "validation") {
         // handle error
    }
    // re-throw, or whatever else
}

1
물건을 던지는 것은 좋은 생각이 아닙니다. 당신은 더 가지고 error.stack, 표준 금형 것 그것으로하지 작업, 등 등이 방법은 오류 인스턴스에 속성을 추가하는 것, 예를 들어, 더 나은을var e = new Error(); e.type = "validation"; ...
timruffles 없습니다

0

맞춤 오류 데코레이터

이것은 George Bailey의 답변을 기반으로합니다. 하지만 원래 아이디어를 확장하고 단순화합니다. CoffeeScript로 작성되었지만 JavaScript로 쉽게 변환 할 수 있습니다. 아이디어는 그것을 감싸는 데코레이터로 Bailey의 사용자 정의 오류를 확장하여 새로운 사용자 정의 오류를 쉽게 만들 수 있도록하는 것입니다.

참고 : 이것은 V8에서만 작동합니다. Error.captureStackTrace다른 환경 에서는 지원되지 않습니다 .

밝히다

데코레이터는 오류 유형의 이름을 사용하여 오류 메시지를 가져오고 오류 이름을 묶는 함수를 반환합니다.

CoreError = (@message) ->

    @constructor.prototype.__proto__ = Error.prototype
    Error.captureStackTrace @, @constructor
    @name = @constructor.name

BaseError = (type) ->

    (message) -> new CoreError "#{ type }Error: #{ message }"

사용하다

이제 새 오류 유형을 만드는 것이 간단합니다.

StorageError   = BaseError "Storage"
SignatureError = BaseError "Signature"

재미있게, SignatureError너무 많은 인수로 호출 된 경우 를 던지는 함수를 정의 할 수 있습니다 .

f = -> throw SignatureError "too many args" if arguments.length

이것은 꽤 잘 테스트되었으며 V8에서 완벽하게 작동하고 트레이스 백, 위치 등을 유지하는 것처럼 보입니다.

참고 : new사용자 지정 오류를 구성 할 때는 사용 은 선택 사항입니다.


0

오류에 대한 성능에 신경 쓰지 않으면 이것이 할 수있는 가장 작은 것

Object.setPrototypeOf(MyError.prototype, Error.prototype)
function MyError(message) {
    const error = new Error(message)
    Object.setPrototypeOf(error, MyError.prototype);
    return error
}

새로운 MyError (message)없이 사용할 수 있습니다.

Error 생성자가 생성 된 후 프로토 타입을 변경하면 콜 스택과 메시지를 설정할 필요가 없습니다.


0

모센는 ES6에서 위의 좋은 답을 가지고 그 세트의 이름,하지만 당신은 타이프 라이터를 사용하거나하는 경우 만약 당신이 미래에있어 생활 곳에 희망이 공공 및 민간 클래스 필드에 대한 제안 3 단계 만들어졌습니다. ECMAScript / JavaScript의 일부로 4 단계에 들어가면 이것이 조금 짧다는 것을 알고 싶을 것입니다. 3 단계는 브라우저가 기능 구현을 시작하는 곳이므로 브라우저에서 지원하는 경우 아래 코드가 작동 할 수 있습니다. (새로운 Edge 브라우저 v81에서 테스트되었으며 제대로 작동하는 것 같습니다). 이 기능은 현재 불안정한 기능이지만주의해서 사용해야하며 불안정한 기능에 대한 브라우저 지원을 항상 확인해야합니다. 이 게시물은 주로 브라우저가이를 지원할 수있는 미래의 거주자를위한 것입니다. 지원 확인을 확인 MDN수 있습니까 사용 . 현재 브라우저 시장에서 66 %의 지원을 받고 있지만 그다지 훌륭하지는 않으므로 지금 사용하고 Babel 과 같은 transpiler 또는 TypeScript와 같은 변환기를 사용하지 않으려면 기다리 십시오 .

class EOFError extends Error { 
  name="EOFError"
}
throw new EOFError("Oops errored");

던질 때 이름이 기록되지 않는 이름없는 오류와 비교하십시오.

class NamelessEOFError extends Error {}
throw new NamelessEOFError("Oops errored");

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.