Waarom werkt instanceof niet op instances van Error-subklassen onder babel-node?

Ik zie dat de operator instanceofniet werkt op subklassen van Error, wanneer deze wordt uitgevoerd onder babel-nodeversie 6.1.18/Node versie 5.1.0 op OS X. Waarom is dit? Dezelfde code werkt goed in de browser, probeer als voorbeeld mijn viool.

De volgende code voert trueuit in de browser, terwijl deze onder babel-node onwaar is:

class Sub extends Error {
}
let s = new Sub()
console.log(`The variable 's' is an instance of Sub: ${s instanceof Sub}`)

Ik kan me alleen maar voorstellen dat dit te wijten is aan een bug in babel-node, aangezien instanceofwerkt voor andere basisklassen dan Error.

.babelrc

{
  "presets": ["es2015"]
}

Gecompileerde uitvoer

Dit is het JavaScript samengesteld door babel 6.1.18:

'use strict';
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var Sub = (function (_Error) {
  _inherits(Sub, _Error);
  function Sub() {
    _classCallCheck(this, Sub);
    return _possibleConstructorReturn(this, Object.getPrototypeOf(Sub).apply(this, arguments));
  }
  return Sub;
})(Error);
var s = new Sub();
console.log('The variable \'s\' is an instance of Sub: ' + (s instanceof Sub));

Antwoord 1, autoriteit 100%

tl;dr Als je Babel 6 gebruikt, kun je https://www gebruiken .npmjs.com/package/babel-plugin-transform-builtin-extend

Uitbreiding van ingebouwde typen zoals Arrayen Erroren dergelijke is nooit ondersteund in Babel. Het is perfect geldig in een echte ES6-omgeving, maar er zijn vereisten om het te laten werken die erg moeilijk te transpileren zijn op een manier die compatibel is met oudere browsers. Het “werkte” in Babel 5 in die zin dat het geen fout veroorzaakte, maar objecten die werden geïnstantieerd vanuit de uitgebreide subklasse werkten niet zoals ze zouden moeten, bijvoorbeeld:

class MyError extends Error {}
var e1 = new MyError();
var e2 = new Error();
console.log('e1', 'stack' in e1);
console.log('e2', 'stack' in e2);

resulteert in

e1 false
e2 true

Hoewel er geen fout is opgetreden, krijgt de subklasse niet de juiste ‘stack’ zoals fouten zouden moeten doen. Evenzo, als u Arrayzou uitbreiden, zou het zich enigszins als een array kunnen gedragen en array-methoden hebben, maar het gedroeg zich niet volledig als een array.

De Babel 5-documentatie noemde dit specifiek als een randgeval van klassen om op te letten.

In Babel 6 zijn klassen gewijzigd om meer aan de specificaties te voldoen in de manier waarop subklassen worden afgehandeld, en een neveneffect daarvan is dat de bovenstaande code nu nog steedsniet zal werken, maar het zal wel niet op een andere manier werken dan voorheen. Dit is behandeld in https://phabricator.babeljs.io/T3083, maar ik zal hier ingaan op een mogelijke oplossing.

Om het gedrag van Babel 5 subklassen terug te geven (wat nog steeds niet goed of aanbevolen is), kun je de ingebouwde constructor in je eigen tijdelijke klasse plaatsen, bijvoorbeeld

function ExtendableBuiltin(cls){
    function ExtendableBuiltin(){
        cls.apply(this, arguments);
    }
    ExtendableBuiltin.prototype = Object.create(cls.prototype);
    Object.setPrototypeOf(ExtendableBuiltin, cls);
    return ExtendableBuiltin;
}

Met deze helper, in plaats van te doen

class MyError extends Error {}

doen

class MyError extends ExtendableBuiltin(Error) {}

In jouw specifieke geval heb je echter gezegd dat je op Node 5.x zit. Node 5 heeft ondersteuning voor native ES6-klassen zonder transpiling. Ik raad je aan die te gebruiken door de voorinstelling es2015te verwijderen en in plaats daarvan node5zodat je onder andere native lessen krijgt. In die context,

class MyError extends Error {}

werkt zoals u verwacht.

Voor mensen die geen gebruik maken van Node 4/5, of alleen recent Chrome, kun je overwegen iets als https://www te gebruiken .npmjs.com/package/error. U kunt ook https://www.npmjs.com/package/babel-plugin-transform- verkennen ingebouwde uitbreiding. De optie approximatedaarvan is hetzelfde gedrag als bij Babel 5. Pas op dat het gedrag dat niet approximateis, absoluut edge-casey is en mogelijk niet in 100% van de gevallen werkt.


Antwoord 2, autoriteit 9%

instanceofwerkt niet voor fouten in subklassen als het compileerdoel is ingesteld op “es5”. Ik heb het doel ingesteld op “es6” in mijn tsconfig.jsonen instanceofproduceerde het juiste resultaat.

Other episodes