1. ホーム
  2. javascript

[解決済み] JavaScriptで拡張メソッドを書くには?

2022-10-12 21:37:19

質問

JSでいくつかの拡張メソッドを記述する必要があります。 私はC#でこれを行う方法だけを知っています。 例です。

public static string SayHi(this Object name)
{
    return "Hi " + name + "!";
}

で呼び出されます。

string firstName = "Bob";
string hi = firstName.SayHi();

JavaScriptでこのようなことをするにはどうしたらよいでしょうか?

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

JavaScriptには、C#の拡張メソッドの正確なアナログはありません。JavaScriptとC#は全く異なる言語なのです。

最も近い類似のものは、すべての文字列オブジェクトのプロトタイプオブジェクトを変更することです。 String.prototype . 一般に、ベストプラクティスは ではなく であり、あなたがコントロールしない他のコードと結合されることを意図したライブラリコード内の組み込みオブジェクトのプロトタイプを変更することです。(どのような他のコードがアプリケーションに含まれるかを制御するアプリケーションでそれを行うことは問題ありません。)

もしあなたが を行う を変更する場合、そのプロトタイプを 非列挙型 プロパティを Object.defineProperty (ES5+、つまり基本的に最新のJavaScript環境、IE8¹以前は不可)を使用します。他の文字列メソッドの列挙可能性、書き込み可能性、および設定可能性に合わせるために、次のようになります。

Object.defineProperty(String.prototype, "SayHi", {
    value: function SayHi() {
        return "Hi " + this + "!";
    },
    writable: true,
    configurable: true
});

(デフォルトは enumerablefalse .)

古い環境をサポートする必要がある場合は String.prototype については、特に、列挙可能なプロパティを作成することで、おそらくうまくいくでしょう。

// Don't do this if you can use `Object.defineProperty`
String.prototype.SayHi = function SayHi() {
    return "Hi " + this + "!";
};

それは良くないことですが、逃げられるかもしれません。 決して でやってください。 Array.prototype または Object.prototype これらのプロパティに列挙可能なプロパティを作成することは、Bad Thing™です。

詳細はこちら。

JavaScriptはプロトタイプの言語です。つまり、すべてのオブジェクトのバックには プロトタイプオブジェクト . JavaScriptでは、そのプロトタイプは4つの方法のうちの1つで代入されます。

  • によって コンストラクタ関数 をオブジェクトのために使用します (例, new Foo でオブジェクトを生成します。 Foo.prototype をプロトタイプとするオブジェクトを生成します)。
  • によって Object.create という関数が ES5 (2009)で追加されました。
  • によって __proto__ アクセッサプロパティ(ES2015+、ウェブブラウザのみ、標準化される前から一部の環境に存在)または Object.setPrototypeOf (を使用することができます(ES2015+)。
  • プリミティブのメソッドを呼び出すために、JavaScriptエンジンがプリミティブのオブジェクトを作成するとき(これは、"promotion"と呼ばれることもあります。)

つまり、あなたの例では firstName は文字列プリミティブなので、これは String インスタンスに昇格します。 String インスタンスのプロトタイプは String.prototype . ですから、プロパティを String.prototype を参照するプロパティを SayHi 関数を参照すると、その関数はすべての String インスタンスで利用できるようになります (文字列プリミティブは昇格するので、実質的には文字列プリミティブで)。

例を挙げます。

Object.defineProperty(String.prototype, "SayHi", {
    value: function SayHi() {
        return "Hi " + this + "!";
    },
    writable: true,
    configurable: true
});

console.log("Charlie".SayHi());

C#の拡張メソッドとは、いくつかの重要な違いがあります。

が動作します(ただし YourExtensionMethod を受け取ったときにスローします。 null をインスタンスパラメータとして受け取ったとき) を投げます。JavaScriptではそうはいきません。 null はそれ自身の型であり、どのプロパティ参照も null のプロパティ参照はエラーを投げます。(そうでなかったとしても、Null 型のために拡張するプロトタイプはありません)。

  • (として クリス がコメントで指摘したように) C#の拡張メソッドはグローバルではありません。拡張メソッドを使用するコードで定義された名前空間が使用されている場合にのみアクセス可能です。(拡張メソッドは静的呼び出しのための構文上の糖分であり、そのため拡張メソッドは null .) JavaScriptではそれは当てはまりません。組み込みのプロトタイプを変更すると、その変更は すべて のコードで見られることになります (レルムとは、グローバル環境とそれに関連する固有オブジェクトなどのことです)。ですから、もしあなたがウェブページでこれを行うなら すべて を実行すると、そのページで読み込んだコードが変更されます。これをNode.jsのモジュールでやると。 すべて のコードで読み込まれた場合、そのモジュールと同じ領域で読み込まれたコードは変更を見ます。どちらの場合も、これがライブラリコードでこれを行わない理由です。(ウェブワーカーと Node.js ワーカスレッドは独自のレルムにロードされるので、メインスレッドとは異なるグローバル環境と異なるイントリンジックスを持っています。しかし、そのレルムはまだすべてのモジュールと共有されています それらは をロードします)。

¹ IE8 には Object.defineProperty がありますが、これは DOM オブジェクトにのみ作用し、JavaScript オブジェクトには作用しません。 String.prototype はJavaScriptオブジェクトです。