ES6クラスインスタンスのクラス名を取得する


157

ES6クラスインスタンスからクラス名を取得する「調和のとれた」方法はありますか?以外

someClassInstance.constructor.name

現在、私はTraceurの実装を期待しています。そして、Babelにはポリフィルがあるようですが、Function.nameTraceurにはありません。

まとめると、ES6 / ES2015 / Harmonyには他に方法がなく、ES.NextにはATMが期待されていません。

それは非縮小化されたサーバー側アプリケーションに有用なパターンを提供するかもしれませんが、ブラウザ/デスクトップ/モバイル向けのアプリケーションでは望ましくありません。

Babel core-js polyfillを使用します。Traceur Function.nameおよびTypeScriptアプリケーションでは、必要に応じて手動でロードする必要があります。


2
私は同じ問題に出くわしました。Traceurの場合、唯一の解決策は、実際のクラスコード自体を解析して名前を抽出することでした。これは、調和があるとは見なされません。私は錠剤を飲み込んでバベルに切り替えました。Traceurの開発はやや停滞しているようで、ES6の多くの機能は十分に実装されていません。あなたが述べたように、適切なES6でクラス名instance.constructor.nameclass.name返します。
Andrew Odri 2015年

唯一の方法のようです。
Frederik Krautwald

これはES6標準ですか?
drudru

12
someClassInstance.constructor.nameあなたのコードを醜くした場合、それが壊滅的になることに言及する価値があります。
JamesB

stackoverflow.com/questions/2648293/…これを見てみたいかもしれませんが、w /で動作するはず.constructorです。
Florrie

回答:


206

someClassInstance.constructor.nameこれはまさに正しい方法です。トランスパイラーはこれをサポートしない場合がありますが、これは仕様による標準的な方法です。(nameClassDeclarationプロダクションを介して宣言された関数のプロパティは14.5.15のステップ6で設定されます。)


2
それは私が恐れていたものです。そのための適切なポリフィルを知っていますか?私はBabelがそれをどのように行うかを理解しようとしましたが、ほとんど成功しませんでした。
Estus Flask

言語機能(クラス)のポリフィルの意味がわかりません。
Domenic 2015年

つまり、constructor.nameのポリフィルです。Babelはそれを実装しているようですが、それがどのように実行されるのか理解できませんでした。
Estus Flask

2
@estus someClassInstance.constructorは関数です。すべての関数には、nameその名前に設定されたプロパティがあります。だからこそ、バベルは何もする必要がありません。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…を
エステバン

2
@Esteban Babelがコア-jsポリフィル(Function.nameのポリフィルを含む)を永続的に宣伝しているようです。そのため、一部のBabelビルドはすべてのブラウザーでそのまま使用できます。
Estus Flask 2016

53

@Domenicが言うように、を使用してくださいsomeClassInstance.constructor.name。@Estebanはコメントで言及しています

someClassInstance.constructor関数です。すべての関数にはnameプロパティがあります...

したがって、クラス名に静的にアクセスするには、次のようにします(これは私のBabelバージョンBTWで機能します。@ Domenicのコメントによると、走行距離は異なる場合があります)。

class SomeClass {
  constructor() {}
}

var someClassInstance = new SomeClass();
someClassInstance.constructor.name;      // === 'SomeClass'
SomeClass.name                           // === 'SomeClass'

更新

Babelは問題ありませんでしたが、uglify / minificationで問題が発生しました。私はゲームを作成しており、プールされたSpriteリソースのハッシュを作成しています(キーは関数名です)。縮小後、すべての関数/クラスに名前が付けられましたt。これはハッシュを殺します。私はGulpこのプロジェクトで使用しています。gulp-uglifyのドキュメントを読んだ後、このローカル変数/関数名のマングリングが発生しないようにするパラメーターがあることがわかりました。だから、私のgulpfileで私は変更しました

.pipe($.uglify()).pipe($.uglify({ mangle: false }))

ここでは、パフォーマンスと読みやすさのトレードオフがあります。名前をマングルしないと、ビルドファイルが(わずかに)大きくなり(ネットワークリソースが増え)、コードの実行が遅くなる可能性があります(引用が必要-BSの場合があります)。一方、同じままにしておくと、getClassName静的およびインスタンスレベルで、すべてのES6クラスを手動で定義する必要があります。結構です!

更新

コメントでの議論の後、.nameそれらの関数を定義するために規約を回避することは良いパラダイムのようです。ほんの数行のコードしか必要とせず、コードの完全な小型化と一般性を可能にします(ライブラリで使用する場合)。だから私は自分の考えを変え、getClassName自分のクラスで手動で定義すると思います。@estusに感謝。とにかく、特にクライアントベースのアプリケーションでは、直接変数アクセスと比較して、ゲッター/セッターは良いアイデアです。

class SomeClass {
  constructor() {}
  static getClassName(){ return 'SomeClass'; }
  getClassName(){ return SomeClass.getClassName(); }
}
var someClassInstance = new SomeClass();
someClassInstance.constructor.getClassName();      // === 'SomeClass' (static fn)
someClassInstance.getClassName();                  // === 'SomeClass' (instance fn)
SomeClass.getClassName()                           // === 'SomeClass' (static fn)

3
マングリングは縮小に大きく貢献するため、マングリングを完全に無効にすることはあまり良い考えではありません。そもそもクライアント側コードでサブジェクトを使用することはあまり良い考えではありませんが、クラスはreservedUglifyオプションを使用してマングリングから保護できます(クラスのリストは正規表現などで取得できます)。
Estus Flask 2016

とても本当です。ファイルサイズを大きくすることにはトレードオフがあります。これは、RegExを使用して、選択したアイテムのみのマングリングを防ぐ方法です。「件名をクライアント側のコードで使用することもあまり良い考えではない」とはどういう意味ですか?一部のシナリオでセキュリティリスクが発生しますか?
James L.

1
いいえ、すでに言われたことだけです。クライアント側のJSが縮小されるのは通常のことなので、このパターンがアプリに問題を引き起こすことはすでにわかっています。きちんとしたnameパターンの代わりに、クラス文字列識別子のコードを1行余分に追加するだけで、メリットが得られる場合があります。同じことがノードに適用される可能性がありますが、適用範囲は狭くなります(たとえば、難読化されたElectronアプリ)。経験則として、私はnameサーバーのコードに依存しますが、ブラウザーのコードや共通ライブラリには依存しません。
Estus Flask 2016

わかりましたので、2つのgetClassName関数(静的およびインスタンス)を手動で定義して地獄を回避し、完全なミニファイを可能にする(煩わしいRegExなしで)ことをお勧めします。ライブラリについてのその点は非常に理にかなっています。理にかなっています。私にとって、私のプロジェクトは自作の小さなCordovaアプリなので、これらは実際には問題ではありません。それを超えると、これらの問題が発生しているのがわかります。議論をありがとう!投稿の改善点について考えられる場合は、自由に編集してください。
ジェームズL.

1
はい、私は最初にnameクラス(サービス、プラグインなど)を使用するエンティティの名前をクラス名から抽出するためにDRYerコードを使用しましたが、最終的には静的プロップ(id_name)で明示的に複製していることがわかりましたこれは最も堅実なアプローチです。良い代替案は、クラス名を気にせず、クラス自体(関数オブジェクト)を、このクラスにマッピングされているエンティティの識別子としてimport使用し、必要に応じて(Angular 2 DIで使用されていた方法)です。
Estus Flask 2016

8

クラスから直接クラス名を取得する

以前の答えはそれがsomeClassInstance.constructor.nameうまく機能することを説明しましたが、プログラムでクラス名を文字列に変換する必要があり、そのためのインスタンスを作成したくない場合は、次のことに注意してください:

typeof YourClass === "function"

また、すべての関数にはnameプロパティがあるため、クラス名を含む文字列を取得するもう1つの方法は、次のようにすることです。

YourClass.name

以下は、これが役立つ理由の良い例です。

Webコンポーネントの読み込み

MDNのドキュメントは、私たちを教え、これは、Webコンポーネントをロードする方法です。

customElements.define("your-component", YourComponent);

YourComponentから拡張されたクラスはどこにありHTMLElementますか。コンポーネントタグ自体にちなんでコンポーネントのクラスに名前を付けることは良い習慣であると考えられているため、すべてのコンポーネントが自分自身を登録するために使用できるヘルパー関数を書くとよいでしょう。これがその関数です:

function registerComponent(componentClass) {
    const componentName = upperCamelCaseToSnakeCase(componentClass.name);
    customElements.define(componentName, componentClass);
}

だからあなたがする必要があるのは:

registerComponent(YourComponent);

これは、コンポーネントタグを自分で書くよりもエラーが発生しにくいため、便利です。まとめると、次のupperCamelCaseToSnakeCase()関数です。

// converts `YourString` into `your-string`
function upperCamelCaseToSnakeCase(value) {
    return value
        // first char to lower case
        .replace(/^([A-Z])/, $1 => $1.toLowerCase())
        // following upper chars get preceded with a dash
        .replace(/([A-Z])/g, $1 => "-" + $1.toLowerCase());
}

ありがとう。例はクライアント側です。すでに述べたように、ブラウザでの関数名の使用にはいくつかの問題があります。ほとんどすべてのブラウザのコードが縮小されると予想されますが、これは関数名に依存するコードを台無しにします。
Estus Flask 2018

はい、あなたは完全に正しいです。このアプローチを機能させるには、クラス名に触れないように縮小版を構成する必要があります。
Lucio Paiva 2018

1

バベル蒸散用(縮小前)

でBabelを使用している場合は@babel/preset-env、クラスの定義を関数に変換せずに保持できます(constructorプロパティを削除します)。

次のように、この構成との古いブラウザ互換性をいくつか削除できますbabel.config / babelrc

{
  "presets": [
    ["@babel/preset-env", {"targets": {"browsers": ["> 2%"]}}]
  ]
}

詳細情報targetshttps : //babeljs.io/docs/en/babel-preset-env#targets

バベルミニファイ(熱分解後)

今のところ簡単な解決策はないようです...除外のマングル化を検討する必要があります。


これが縮小にどのように役立つと思われるかをさらに説明できますか?任意のターゲットのclass Foo {}ようなものに縮小されますclass a{}Foo縮小されたソースコードには言葉はありません。
Estus Flask

正直に言って、私はドキュメンテーションとそれがこの構成を使用するのを助けたという事実以上のものを掘りませんでした...私はECSYをバベル変換プロジェクトに使用していて、有効なクラス名を取得するためにこのパラメーターが必要です:github.com/MozillaReality/ecsy/issues/ 119
IFNOT

そうですか。これは、扱ったコードに非常に特有です。たとえば、ESモジュールとES6の名前は保持export class Foo{}できません。これ以上効率的に活用することはできませんが、これは他の場所では異なる場合があるため、ビルド時に特定のコードで何が起こるかよくわからなければ、正確に知ることができません。とにかく、これは2015年以降変更されていません。一部のコンパイル構成とコードでは常に可能でした。そして、この可能性はまだ脆弱であり、アプリのロジックにクラス名を使用することはできません。信頼できるクラス名は、ソースコードが誤って変更された後、
不要に

1
わかりましたコードを見て、何が起こっているのかを見つけました。私の解決策は、クラスの関数への変換を修正します。つまり、縮小化の前に役立ちます。しかし、ミニファイの問題ではありません。掘り下げて続けなければならないのは、使用しconstructor.nameているすべてのコードが縮小版でどのように機能するか理解できなかったためです...
illogical
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.