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
});
(のデフォルトはenumerable
ですfalse
。)
廃止された環境をサポートする必要がある場合はString.prototype
、具体的には、列挙可能なプロパティの作成を回避できる可能性があります。
// Don't do this if you can use `Object.defineProperty`
String.prototype.SayHi = function SayHi() {
return "Hi " + this + "!";
};
それは良い考えではありませんが、あなたはそれでうまくいくかもしれません。またはでそれをしないでください; それらに列挙可能なプロパティを作成するのはBadThing™です。Array.prototype
Object.prototype
詳細:
JavaScriptは典型的な言語です。これは、すべてのオブジェクトがプロトタイプオブジェクトによってサポートされていることを意味します。JavaScriptでは、そのプロトタイプは次の4つの方法のいずれかで割り当てられます。
new Foo
でオブジェクトを作成しFoo.prototype
、そのプロトタイプとして)Object.create
ES5(2009)で追加された機能による__proto__
アクセサプロパティ(ES2015 +、唯一のWebブラウザ上で、それが標準化された前に、いくつかの環境に存在していた)またはObject.setPrototypeOf
(ES2015 +)したがって、あなたの例でfirstName
は、は文字列プリミティブであるためString
、メソッドを呼び出すたびにインスタンスにプロモートされ、そのString
インスタンスのプロトタイプはString.prototype
です。したがってString.prototype
、SayHi
関数を参照するプロパティを追加すると、その関数がすべての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.prototype
JavaScriptオブジェクトです。
String s = null;
部分の要点は使用することでしたs.YourExtensionMethod()
!キャッチに感謝します。:-)
class MyArray extends Array { singleOrDefault() { ... }}
)を作成することもできますが、が退屈な古い配列のMyArray.from(originalArray)
場合originalArray
は、使用する前に行う必要があります。JavaScript用のLINQのようなライブラリもあります(これはまとめです)。これは、何かを行う前に配列からLinqオブジェクトを作成することを同様に含みます(まあ、LINQ to JavaScriptは、他のものを使用していません[そしてそれを使用していません]年間で])。(BTW、回答を更新、thx。)
グローバル拡張の使用
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
};