2015-05-22 5 views
11

Quando si esegue questo frammento attraverso BabelJS:errori estesi non hanno un messaggio o una pila di traccia

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

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

essa stampa

{} 

che non è quello che mi aspettavo. Esecuzione

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

produce

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

che è esattamente quello che vorrei da errore esteso.

Il mio obiettivo è estendere Error in una varietà di sottoclassi e usarle nella corrispondenza di bluebird catch. Finora, questo sta fallendo miseramente.

Perché la sottoclasse non mostra un messaggio o traccia dello stack?

Modifica:using Chrome's built-in subclassing (grazie a @coder) funziona perfettamente. Questo non è specifico per Babel, necessariamente, come nel seguente esempio (da @loganfsmyth on Babel's gitter feed) mostra:

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

Non penso che sia un problema di Babele. Se utilizzi il vecchio modo di estendere l'errore, ottieni lo stesso stack mancante (su Chromium41). –

+1

puoi confermare il browser che stai utilizzando? sembra funzionare per chrome v42 https://jsfiddle.net/5e3kakqj/ – coder

+1

@coder su Chrome 42 pure. Il tuo esempio funziona, ma la versione Babel no. – ssube

risposta

9

In breve, si estende utilizzando il codice transpiled di babele funziona solo per le classi costruite in modo specifico, e un sacco di nativi roba non sembra essere costruita in quel modo. I documenti di Babel avvertono che l'estensione di molte classi native non funziona correttamente.

Si potrebbe creare una classe di buffer che crea le proprietà "manualmente", qualcosa di simile:

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

} 

quindi estendere tale classe, invece:

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

Perché non lo fa funziona come ti aspetteresti?

Se si guarda a ciò che è transpiled, vedrai che babel prima assegna una copia della classe super prototipo alla classe sub, poi quando si chiama new SubClass() questa funzione si chiama:

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

Dove _GET è una funzione di supporto iniettato nello 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); 
    } 
}); 

fa qualcosa come trova il descrittore constructor proprietà della classe sub il prototipo del prototipo e ha cercato di chiamare il suo wi getter th la nuova istanza di sottoclasse come context se esiste o restituisce il suo valore (if ("value" in desc)), in questo caso il costruttore Error stesso. Non assegna nulla a this dalla super-chiamata, quindi mentre il nuovo oggetto ha il prototipo giusto, non è stato costruito come previsto. Fondamentalmente la super chiamata non fa nulla per l'oggetto appena costruito, crea solo un nuovo Error che non è assegnato a nulla.

Se si utilizza lo ErrorClass definito sopra, esso aderisce alla struttura della classe come previsto da Babel.

+0

Questo sembra giusto. È possibile estendere solo determinati oggetti nativi usando la sintassi nativa. – ssube

+0

I (penso) funzionerà correttamente quando le classi ES2015 sono in esecuzione nativamente nei browser (vs transpiled a ES5, poiché alcune funzionalità sono impossibili da shim). – trusktr