Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
216 views
in Technique[技术] by (71.8m points)

javascript - Extended Errors do not have message or stack trace

When running this snippet through BabelJS:

class FooError extends Error {
  constructor(message) {
    super(message);
  }
}

let error = new FooError('foo');
console.log(error, error.message, error.stack);

it outputs

{}

which is not what I expect. Running

error = new Error('foo');
console.log(error, error.message, error.stack);

produces

{} foo Error: foo
    at eval (eval at <anonymous> (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:263:11), <anonymous>:24:9)
    at REPL.evaluate (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:263:36)
    at REPL.compile (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:210:12)
    at Array.onSourceChange (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:288:12)
    at u (https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js:28:185)

which is exactly what I would like from the extended error.

My goal is to extend Error into a variety of subclasses and use them in bluebird's catch matching. So far, that is failing miserably.

Why is the subclass not showing a message or stack trace?

Edit: using Chrome's built-in subclassing (thanks to @coder) works perfectly. This isn't specific to Babel, necessarily, as the following example (from @loganfsmyth on Babel's gitter feed) shows:

// Works
new (function(){
  "use strict";
  return class E extends Error { }
}());
// Doesn't
new (function(){
  "use strict";
  function E(message){
    Error.call(this, message);
  };
  E.prototype = Object.create(Error);
  E.prototype.constructor = E;
  return E;
}());
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In short, extending using babel's transpiled code only works for classes built in a specific way, and a lot of native stuff doesn't appear to be built like that. Babel's docs warns that extending many native classes doesn't work properly.

You could create a buffer class that creates the properties "manually", something like this:

class ErrorClass extends Error {
  constructor (message) {
    super();

    if (Error.hasOwnProperty('captureStackTrace'))
        Error.captureStackTrace(this, this.constructor);
    else
       Object.defineProperty(this, 'stack', {
          value: (new Error()).stack
      });

    Object.defineProperty(this, 'message', {
      value: message
    });
  }

}

Then extend that class instead:

class FooError extends ErrorClass {
  constructor(message) {
    super(message);
  }
}

Why doesn't it work as you'd expect?

If you look at what is transpiled, you'll see that babel first assigns a copy of the super class' prototype to the sub class, then when you call new SubClass() this function is called:

_get(Object.getPrototypeOf(FooError.prototype), "constructor", this).call(this, message)

Where _get is a helper function injected into the script:

(function get(object, property, receiver) {
  var desc = Object.getOwnPropertyDescriptor(object, property);

  if (desc === undefined) {
    var parent = Object.getPrototypeOf(object);

    if (parent === null) {
      return undefined;
    } else {
      return get(parent, property, receiver);
    }
  } else if ("value" in desc) {
    return desc.value;
  } else {
    var getter = desc.get;

    if (getter === undefined) {
      return undefined;
    }

    return getter.call(receiver);
  }
});

it does something like finds the constructor property descriptor of the sub class' prototype's prototype and tried to call its getter with the new subclass instance as context if it exists or return its value (if ("value" in desc)), in this case the Error constructor itself. It doesn't assign anything to this from the super call so while the new object has the right prototype, it didn't get constructed the way you expect. Basically the super call does nothing to the newly constructed object, just creates a new Error which isn't assigned to anything.

If we use the ErrorClass defined above, it does adhere to the class structure as expected by Babel.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...