JavaScriptで拡張メソッドを作成するにはどうすればよいですか?


83

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

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

その後、によって呼び出されます:

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

JavaScriptでこのようなことをするにはどうすればよいですか?

回答:


149

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

(のデフォルトはenumerableですfalse。)

廃止された環境をサポートする必要がある場合はString.prototype、具体的には、列挙可能なプロパティの作成を回避できる可能性があります。

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

それは良い考えではありませんが、あなたはそれでうまくいくかもしれません。またはでそれをしないでください; それらに列挙可能なプロパティを作成するのはBadThing™です。Array.prototypeObject.prototype

詳細:

JavaScriptは典型的な言語です。これは、すべてのオブジェクトがプロトタイプオブジェクトによってサポートされていることを意味します。JavaScriptでは、そのプロトタイプは次の4つの方法のいずれかで割り当てられます。

  • コンストラクタ関数オブジェクトのための(例えば、new Fooでオブジェクトを作成しFoo.prototype、そのプロトタイプとして)
  • Object.createES5(2009)で追加された機能による
  • よる__proto__アクセサプロパティ(ES2015 +、唯一のWebブラウザ上で、それが標準化された前に、いくつかの環境に存在していた)またはObject.setPrototypeOf(ES2015 +)
  • プリミティブのオブジェクトを作成するときにJavaScriptエンジンによってメソッドを呼び出しているため(これは「プロモーション」と呼ばれることもあります)

したがって、あなたの例でfirstNameは、は文字列プリミティブであるためString、メソッドを呼び出すたびにインスタンスにプロモートされ、そのStringインスタンスのプロトタイプはString.prototypeです。したがってString.prototypeSayHi関数を参照するプロパティを追加すると、その関数がすべてのStringインスタンスで使用できるようになります(また、文字列プリミティブはプロモートされるため、効果的に使用できます)。

例:

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

  • DougRがコメントで指摘したように) C#の拡張メソッドnull参照で呼び出すことができますstring拡張メソッドがある場合は、次のコードを使用します。

    string s = null;
    s.YourExtensionMethod();
    

    動作します。これはJavaScriptには当てはまりません。nullは独自の型であり、上のプロパティ参照nullはエラーをスローします。(そうでなかったとしても、Nullタイプ用に拡張するプロトタイプはありません。)

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


¹IE8にはがObject.definePropertyありますが、JavaScriptオブジェクトではなく、DOMオブジェクトでのみ機能します。String.prototypeJavaScriptオブジェクトです。


@DougR:申し訳ありませんが、標準のコメントのクリーンアップです。コメントが廃止された場合、modまたはパワーユーザーはコメントを削除できます(modは直接コメントを実行でき、パワーユーザーはレビューキューでチームを組む必要があります)。私はあなたがこれにフラグを立てたことを呼び出すために答えを更新しました。:-)
TJ Crowder

1
@Grundy:ありがとう、ええ、このString s = null;部分の要点は使用することでしたs.YourExtensionMethod()!キャッチに感謝します。:-)
TJ Crowder

拡張メソッドがnullエンティティに対して機能することを強調していただきありがとうございます。
ラム

1
たとえば、Node.jsでこれを行うと、プログラム内のすべての文字列(他のモジュールを含む)に影響を与える(新しいプロパティを追加する)と思いますか?2つのモジュールが両方とも「SayHi」プロパティを定義している場合、競合しますか?
ChrisW

1
@ ChrisW-かなり。:-)代わりに、Arrayサブクラス(class MyArray extends Array { singleOrDefault() { ... }})を作成することもできますが、が退屈な古い配列のMyArray.from(originalArray)場合originalArrayは、使用する前に行う必要があります。JavaScript用のLINQのようなライブラリもあります(これはまとめです)。これは、何かを行う前に配列からLinqオブジェクトを作成することを同様に含みます(まあ、LINQ to JavaScriptは、他のものを使用していません[そしてそれを使用していません]年間で])。(BTW、回答を更新、thx。)
TJ Crowder 2018

0

グローバル拡張の使用

declare global {
    interface DOMRect {
        contains(x:number,y:number): boolean;
    }
}
/* Adds contain method to class DOMRect */
DOMRect.prototype.contains = function (x:number, y:number) {                    
    if (x >= this.left && x <= this.right &&
        y >= this.top && y <= this.bottom) {
        return true
    }
    return false
};

これはpure / vanilla javascriptで機能しますか、それともtypescriptの使用方法を理解する必要がありますか?あなたはES6ドキュメントまたはMDNをtypescriptlang.orgにリンクして言っていないので、私は聞いてるのよ
user1306322
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.