web-dev-qa-db-fra.com

Pourquoi instanceof ne fonctionne-t-il pas sur des instances de sous-classes Error sous babel-node?

Je constate que l’opérateur instanceof ne fonctionne pas sur les instances de sous-classes Error lorsqu’il est exécuté sous babel-node version 6.1.18/Node version 5.1.0 sur OS X. Pourquoi? Le même code fonctionne bien dans le navigateur, essayez mon violon pour un exemple.

Le code suivant affiche true dans le navigateur, alors que sous babel-node c'est false:

class Sub extends Error {
}

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

Je peux seulement imaginer que cela soit dû à un bogue dans babel-node, puisque instanceof fonctionne pour des classes de base autres que Error.

.babelrc

{
  "presets": ["es2015"]
}

Sortie compilée

Voici le JavaScript compilé par 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));
73
aknuds1

tl; dr Si vous utilisez Babel 6, vous pouvez utiliser https://www.npmjs.com/package/babel-plugin-transform-builtin-extend

L'extension de types intégrés tels que Array et Error n'a jamais été prise en charge dans Babel. Il est parfaitement valable dans un environnement ES6 réel, mais il est très difficile de le transpiler d’une manière compatible avec les navigateurs plus anciens. Cela "fonctionnait" dans Babel 5 en ce sens qu'il ne renvoyait pas d'erreur, mais que les objets instanciés à partir de la sous-classe étendue ne fonctionnaient pas comme ils étaient censés le faire, par exemple:

class MyError extends Error {}

var e1 = new MyError();
var e2 = new Error();

console.log('e1', 'stack' in e1);
console.log('e2', 'stack' in e2);

résulte en

e1 false
e2 true

Bien qu’elle ne soit pas sortie d’erreur, la sous-classe n’obtient pas correctement une "pile" comme le sont supposées les erreurs. De même, si vous étendez Array, il se comportera peut-être un peu comme un tableau et comportera des méthodes de tableau, mais il ne se comportera pas complètement comme un tableau.

La documentation de Babel 5 l'appelait spécifiquement comme un cas Edge de classes à connaître.

Dans Babel 6, les classes ont été modifiées pour être plus conformes à la spécification de la façon dont les sous-classes sont gérées, ce qui a pour effet secondaire de faire en sorte que le code ci-dessus reste toujours ne fonctionnera pas, mais cela ne fonctionnera pas différemment d’avant. Cela a été couvert dans https://phabricator.babeljs.io/T308 , mais je vais expliquer ici une solution potentielle.

Pour renvoyer le comportement de sous-classement de Babel 5 (qui, rappelons-le, n’est toujours ni correct ni recommandé), vous pouvez envelopper le constructeur intégré dans votre propre classe temporaire, par exemple.

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

    return ExtendableBuiltin;
}

Avec cette aide, plutôt que de faire

class MyError extends Error {}

faire

class MyError extends ExtendableBuiltin(Error) {}

Cependant, dans votre cas particulier, vous avez indiqué que vous êtes sur Node 5.x. Node 5 prend en charge les classes natives ES6 sans transpiler. Je vous recommande de les utiliser en laissant tomber le es2015 prérégler et utiliser plutôt node5 vous obtenez donc des cours natifs, entre autres choses. Dans ce contexte,

class MyError extends Error {}

fonctionnera comme vous le souhaitez.

Pour les personnes qui ne sont pas sur Node 4/5, ou récent Chrome uniquement, vous pouvez envisager d’utiliser quelque chose du type https: // www. npmjs.com/package/error . Vous pouvez également explorer https://www.npmjs.com/package/babel-plugin-transform-builtin-extend . Le approximate est le même comportement que Babel 5. Notez que le comportement autre que approximate est définitivement Edge-casey et peut ne pas fonctionner dans 100% des cas.

87
loganfsmyth