web-dev-qa-db-ja.com

変数がES6クラス宣言であるかどうかを確認する方法は?

1つのモジュールから次のES6クラスをエクスポートしています。

export class Thingy {
  hello() {
    console.log("A");
  }

  world() {
    console.log("B");
  }
}

そして、別のモジュールからインポートする:

import {Thingy} from "thingy";

if (isClass(Thingy)) {
  // Do something...
}

変数がクラスかどうかを確認するにはどうすればよいですか?クラスインスタンスではなく、クラス宣言

言い換えると、上記の例でisClass関数をどのように実装しますか?

23

ここで前もって明確にします。任意の関数はコンストラクターになります。 「クラス」と「関数」を区別している場合は、API設計の選択が不十分です。たとえば、何かがclassでなければならない場合、そのコードは代わりに関数に変換されるため、BabelやTypeScriptを使用している人はclassとして検出されません。つまり、コードベースmustを使用するすべての人がES6環境で一般的に実行するように義務付けられているため、コードは古い環境では使用できなくなります。

ここでのオプションは、実装定義の動作に制限されています。 ES6では、コードが解析され、構文が処理されると、クラス固有の動作はほとんど残りません。必要なのは、コンストラクター関数だけです。あなたの最善の選択はすることです

_if (typeof Thingy === 'function'){
  // It's a function, so it definitely can't be an instance.
} else {
  // It could be anything other than a constructor
}
_

また、誰かが非コンストラクター関数を実行する必要がある場合は、そのための別のAPIを公開します。

明らかにそれはあなたが探している答えではありませんが、それを明確にすることが重要です。

ここでの他の答えが言及しているように、クラス宣言を返すには関数の.toString()が必要なため、オプションがあります。

_class Foo {}
Foo.toString() === "class Foo {}" // true
_

ただし、重要なのは、可能な場合のみを適用することです。実装が100%仕様に準拠している

_class Foo{}
Foo.toString() === "throw SyntaxError();"
_

いいえブラウザ現在それを行いますが、たとえばJSプログラミングに焦点を当てた組み込みシステムがいくつかあり、プログラム自体のメモリを保持するために、解析されたソースコードを破棄します。 .toString()から返すソースコードがなく、許可されています。

同様に、.toString()を使用することにより、将来の保証と一般的なAPI設計の両方についての仮定を立てています。あなたが言う

_const isClass = fn => /^\s*class/.test(fn.toString());
_

これは文字列表現に依存しているため、簡単に破損する可能性があります。

デコレータを例に取ります:

_@decorator class Foo {}
Foo.toString() == ???
_

この.toString()にはデコレータが含まれていますか?デコレータ自体がクラスではなくfunctionを返すとどうなりますか?

14
loganfsmyth

値が関数だけでなく、実際にはクラスのコンストラクター関数であることを確認する場合は、関数を文字列に変換し、その表現を検査できます。 仕様では、クラスコンストラクターの文字列表現が規定されています

function isClass(v) {
  return typeof v === 'function' && /^\s*class\s+/.test(v.toString());
}

別の解決策は、値を通常の関数として呼び出すことです。クラスコンストラクターは通常の関数としては呼び出しできませんが、エラーメッセージはおそらくブラウザーによって異なります。

function isClass(v) {
  if (typeof v !== 'function') {
    return false;
  }
  try {
    v();
    return false;
  } catch(error) {
    if (/^Class constructor/.test(error.message)) {
      return true;
    }
    return false;
  }
}

欠点は、関数を呼び出すと、あらゆる種類の未知の副作用が発生する可能性があることです...

27
Felix Kling

たぶんこれは助けることができます

let is_class = (obj) => {
    try {
        new obj();
        return true;
    } catch(e) {
        return false;
    };
};
0
aimadnet