1. ホーム
  2. javascript

[解決済み] babel-nodeでErrorのサブクラスのインスタンスにinstanceofが効かないのはなぜですか?

2023-09-07 04:57:19

質問

私は instanceof のインスタンスでは Error サブクラスのインスタンスでは動作しません。 バベルノード バージョン 6.1.18/Node バージョン 5.1.0 で OS X 上で動作しているときです。これはなぜでしょうか?同じコードがブラウザではうまく動作します。 フィドル を試してみてください。

以下のコードで出力される true を出力しますが、babel-nodeの下ではfalseです。

class Sub extends Error {
}

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

これはbabel-nodeのバグによるものとしか思えません、なぜなら instanceof 以外のベースクラスでは Error .

.babelrc

{
  "presets": ["es2015"]
}

コンパイル出力

babel 6.1.18でコンパイルされたJavaScriptです。

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

どのように解決するのですか?

tl;dr Babel 6を使用している場合は、以下のようになります。 https://www.npmjs.com/package/babel-plugin-transform-builtin-extend

のような組み込み型の拡張 ArrayError などはBabelではサポートされたことがありません。実際のES6環境では完全に有効ですが、古いブラウザと互換性のある方法でトランスパイルするのが非常に困難な、動作させるための要件があります。Babel 5 では、エラーを投げないという点では動作しましたが、たとえば、拡張サブクラスからインスタンス化されたオブジェクトは、想定したようには動作しませんでした。

class MyError extends Error {}

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

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

の結果は

e1 false
e2 true

エラーにはなりませんでしたが、このサブクラスはエラーが想定しているような「スタック」を適切に取得しません。同様に、もしあなたが Array を拡張した場合、それは多少配列のように振る舞い、配列メソッドを持つかもしれませんが、完全に配列のように振る舞うわけではありませんでした。

Babel 5 のドキュメントでは、注意すべきクラスのエッジケースとして、特にこれを呼び出しています。

Babel 6 では、クラスはサブクラスがどのように処理されるかについて、より仕様に準拠するように変更され、その副作用として、現在、上記のコードは まだ は動作しませんが、以前とは異なる方法で動作するわけではありません。これについては https://phabricator.babeljs.io/T3083 で説明しましたが、ここでは潜在的な解決策について詳しく説明します。

Babel 5 のサブクラス化動作を返すために (これはまだ正しくも推奨もされていないことを覚えておいてください)、組み込みコンストラクタを独自の一時的なクラスでラップすることができます。

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

    return ExtendableBuiltin;
}

このヘルパーを使うと

class MyError extends Error {}

する

class MyError extends ExtendableBuiltin(Error) {}

Node 5では、トランスパイルせずにES6ネイティブクラスをサポートします。私は、それらを使用することをお勧めします。 es2015 プリセットを削除して、代わりに node5 を使用することで、特にネイティブクラスを取得することができます。その中で

class MyError extends Error {}

は期待通りに動作します。

Node 4/5でない人、最近のChromeのみの人は、次のようなものを使うことを検討するとよいでしょう。 https://www.npmjs.com/package/error . また https://www.npmjs.com/package/babel-plugin-transform-builtin-extend . は approximate オプションはBabel 5と同じ動作です。注意点としては、非 approximate の動作は間違いなくエッジケースであり、100%のケースで動作しない可能性があることに注意してください。