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

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

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

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



답변

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 자체 제작 오류 객체 비교 .


답변

ES6에서 :

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

출처


답변

한마디로 :

  • 트랜스 파일러없이 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


답변

편집 : 의견을 읽으십시오. 이것은 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

답변

모든 다른 유형의 오류에 대한 상용구피하기 위해 일부 솔루션의 지혜를 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';
});


답변

에서 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__속성은 더 이상 사용되지 않습니다 .


답변

완벽을 기하기 위해-이전의 답변 중 어느 것도이 방법을 언급하지 않았기 때문에-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인스턴스화 됨) 으로 인해 발생합니다 . 작동 방식에 대한 자세한 내용은 여기 에서 설명서를 확인 하십시오 .