読者の中には、図 4 の hasOwnProperty メソッドと isPrototypeOf メソッドの呼び出しに気づいた人もいるかもしれません。これらの手法はどこから来たのでしょうか?これらは Dog.prototype のものではありません。実際、Dog.prototype および Dog インスタンスには、toString、toLocaleString、および valueOf と呼ばれる他のメソッドがありますが、それらはいずれも Dog.prototype からのものではありません。 .NET Framework の System.Object がすべてのクラスの最終的な基本クラスとして機能するのと同じように、JavaScript の Object.prototype はすべてのプロトタイプの最終的な基本プロトタイプです。 (Object.prototype のプロトタイプは null です。)
この例では、Dog.prototype がオブジェクトであることに注意してください。これは、Object コンストラクター (非表示ですが) を呼び出すことによって作成されます。
したがって、Dog インスタンスは Dog.prototype を継承します。 Object.prototype から継承します。これにより、すべての Dog インスタンスも Object.prototype のメソッドとプロパティを継承します。
すべての JavaScript オブジェクトはプロトタイプ チェーンを継承し、すべてのプロトタイプは Object.prototype で終了します。これまでに確認した継承はアクティブなオブジェクト間で行われることに注意してください。これは、宣言時のクラス間の継承を指す一般的な継承の概念とは異なります。したがって、JavaScript の継承はより動的になります。これは、次のような単純なアルゴリズムを使用して行われます。 オブジェクトのプロパティ/メソッドにアクセスしようとすると、JavaScript はそのオブジェクトにプロパティ/メソッドが定義されているかどうかを確認します。そうでない場合は、オブジェクトのプロトタイプがチェックされます。そうでない場合は、オブジェクトのプロトタイプのプロトタイプがチェックされ、Object.prototype に至るまで同様にチェックされます。図 6 は、この解析プロセスを示しています。
プロパティ/メソッド X がオブジェクトに定義されている場合、同じ名前のプロパティ/メソッドはオブジェクトのプロトタイプに隠されます。たとえば、Dog.prototype で toString メソッドを定義することで、Object.prototype の toString メソッドをオーバーライドできます。
図 7 はこれらの効果を示しています。図 7 は、以前に発生した不要なメソッド インスタンスの問題を解決する方法も示しています。プロトタイプ内にメソッドを配置すると、オブジェクトごとに個別の関数オブジェクト インスタンスを持たなくても、オブジェクト間でメソッドを共有できるようになります。この例では、スポットで toString メソッドが何らかの方法でオーバーライドされるまで、ローバーとスポットは getBreed メソッドを共有します。その後、スポットは独自のバージョンの getBreed メソッドを持ちますが、新しい GreatDane で作成されたローバー オブジェクトと後続のオブジェクトは引き続き、GreatDane.prototype オブジェクトで定義された getBreed メソッドのインスタンスを共有します。
場合によっては、インスタンスではなくクラスのプロパティまたはメソッドにバインドする必要があります。 、静的プロパティとメソッド。関数は必要に応じてプロパティとメソッドを設定できるオブジェクトであるため、これは JavaScript で簡単に実行できます。コンストラクターは JavaScript のクラスを表すため、次のようにコンストラクター内で静的メソッドとプロパティを設定することで、クラスに直接追加できます。
新しい日付を返します();
アラート(DateTime.now());
JavaScript で静的メソッドを呼び出すための構文は、C# の構文とほぼ同じです。コンストラクターの名前は実際にはクラスの名前であるため、これは驚くべきことではありません。このように、クラス、パブリック プロパティ/メソッド、静的プロパティ/メソッドがあります。他に何か必要ですか? もちろんプライベート会員も。ただし、JavaScript はプライベート メンバー (同様に保護されたメンバー) をネイティブにサポートしません。誰でもオブジェクトのすべてのプロパティとメソッドにアクセスできます。ただし、クラスにプライベート メンバーを含める方法はありますが、その前に、まずクロージャについて理解する必要があります。
おわりに
私は意識的に JavaScript を学んだことがありません。これがないと、実際の作業で AJAX アプリケーションを作成する準備が整わないことがわかったので、急いで学習する必要がありました。最初は自分のプログラミングレベルが数段階下がったように感じました。 (JavaScript! C の友人は何と言うでしょうか?) しかし、最初のハードルを乗り越えると、JavaScript は実際には強力で、表現力豊かで、非常に簡潔な言語であることがわかりました。他の一般的な言語がサポートし始めたばかりの機能も備えています。
JavaScript のより高度な機能の 1 つはクロージャのサポートです。この機能は、C# 2.0 が匿名メソッドを通じてサポートしています。クロージャは、内部関数 (または C# の内部匿名メソッド) がその外部関数のローカル変数にバインドされるときに発生する実行時現象です。明らかに、この内部関数が何らかの方法で外部関数にアクセスできない限り、あまり意味がありません。この点を例で説明すると分かりやすくなります。
単純な条件に基づいて一連の数値をフィルタリングする必要があるとします。この条件は、100 より大きい数値のみがフィルタを通過でき、残りの数値は無視されます。これを行うには、図 8 のような関数を作成します。
述語に基づいて要素をフィルタリングします
function filter(pred, arr) {
var len = arr.length;
var filtered = []; // new Array() の短縮版
// すべてを反復処理します。配列内の要素 ...
for(var i = 0; i < len; i ) {
var val = arr[i]
// 要素が述語を満たす場合、それを通過させます。
if (pred(val)) {
filtered.push(val);
}
}
フィルタリングされた戻り値;
}
var someRandomNumbers = [12, 32, 1, 3, 2, 2, 234, 236, 632,7, 8];
varnumberGreaterThan100 = filter(
function(x) { return (x > 100) ? true : false; },
someRandomNumbers) ;
// 234、236、632 を表示します
alert(numbersGreaterThan100);
ただし、今回は数値のみであると仮定して、別のフィルター条件を作成します。 300 を超える場合は、次の関数を作成できます。
var greatThan300 = filter(
function(x) { return (x > 300) ? true : false; },
someRandomNumbers); 🎜>その後、50、25、10、600 などをフィルタリングする必要があるかもしれませんが、賢い人であれば、それらはすべて同じ述語「より大きい」を持ち、数値が異なるだけであることがわかるでしょう。したがって、次のような関数を使用して個々の数値を分離できます:
コードをコピーします
コードは次のとおりです: function makeGreaterThanPredicate( lowerBound) {
return function(numberToCheck) {
return (numberToCheck > lowerBound)
}
このようにして、次のコードを書くことができます:
コード
コードをコピー
関数 makeGreaterThanPredicate によって返される内部匿名関数を観察すると、匿名内部関数が makeGreaterThanPredicate に渡されるパラメーターである lowerBound を使用していることがわかります。スコープの一般的な規則に従って、makeGreaterThanPredicate が終了すると、 lowerBound はスコープ外になります。ただし、ここでは、makeGreaterThanPredicate が終了した後も、内部の匿名関数は依然として lowerBound を保持します。これはクロージャと呼ばれるものです。内部関数は、それが定義されている環境 (つまり、外部関数のパラメータとローカル変数) を閉じるためです。
最初はクロージャがそれほど強力であるとは感じないかもしれません。しかし、正しく適用すると、アイデアをコードに変換するのに役立つ、非常に創造的で楽しい方法になります。 JavaScript におけるクロージャの最も興味深い使用法の 1 つは、クラスのプライベート変数をシミュレートすることです。
プライベート プロパティのシミュレーション
次に、クロージャがプライベート メンバーのシミュレーションにどのように役立つかを見てみましょう。通常、関数内のローカル変数には関数の外部からアクセスできません。関数が終了すると、ローカル変数はさまざまな実際的な理由から永久に消えます。ただし、ローカル変数が内部関数のクロージャによってキャプチャされた場合、ローカル変数は存続します。この事実は、JavaScript のプライベート プロパティをエミュレートするための鍵となります。 Person クラスがあるとします。
Code
関数 人 (名前, 年齢) {
this.getName = function() { return name };
this.setName = function(newName) { name = newName }; = function() { return age; };
this.setAge = function(newAge) { age = newAge };
パラメータ名と年齢は、コンストラクターの人物。人物が戻ってきたら、名前と年齢は永久に消えるはずです。ただし、これらは Person インスタンスのメソッドとして割り当てられた 4 つの内部関数によってキャプチャされるため、名前と年齢は事実上存続し続けますが、厳密にはこれら 4 つのメソッドを介してのみアクセスできます。
コード
var ray = 新しい人物("レイ", 31);
alert(ray.getName());
alert(ray.getAge()); Ray”);
// 瞬時に若返ります!
ray.setAge(22);
alert(ray.getName() " は " ray.getAge()
" 歳です。") ;
コンストラクターで初期化されていないプライベート メンバーは、次のようにコンストラクターのローカル変数になることができます:
コードをコピーします。
this.setSoup = function(newOcc) { 職業 =
newOcc }// 名前と年齢のアクセサー
これらはプライベート メンバーであり、C# から期待されるプライベート メンバーとは若干異なることに注意してください。 C# では、クラスのパブリック メソッドはそのプライベート メンバーにアクセスできます。しかし、JavaScript では、プライベート メンバーには、クロージャ内でプライベート メンバーを所有するメソッドを通じてのみアクセスできます (これらのメソッドは通常のパブリック メソッドとは異なるため、特権メソッドと呼ばれることがよくあります)。したがって、Person のパブリック メソッドでは、プライベート メンバーの特権アクセサー メソッドを介してプライベート メンバーにアクセスする必要があります:
コードをコピー
コードは次のとおりです。
;
Douglas Crockford は、プライベート メンバーをシミュレートするためにクロージャーを使用する手法を最初に発見した (またはおそらく公開した) 人として有名です。彼の Web サイト javascript.crockford.com には、JavaScript に関する豊富な情報が含まれており、JavaScript に興味のある開発者は必ず読む必要があります。
クラスからの継承 ここまで、コンストラクターとプロトタイプ オブジェクトを使用して JavaScript でクラスを模擬できる方法を見てきました。プロトタイプ チェーンによって、すべてのオブジェクトが Object.prototype のパブリック メソッドを持つようになり、クラスのプライベート メンバーをシミュレートするためにクロージャを使用する方法がわかりました。しかし、ここには何かが欠けています。 C# では毎日行うことですが、クラスから派生する方法をまだ見ていません。残念ながら、JavaScript でクラスから継承することは、C# でコロンを入力して継承するほど簡単ではなく、より多くの作業が必要になります。一方、JavaScript は非常に柔軟で、さまざまな方法でクラスを継承できます。
たとえば、図 9 に示すように、基本クラス Pet があり、これには派生クラス Dog があります。これを JavaScript で実装するにはどうすればよいでしょうか?ペットクラスは簡単です。実装方法は確認しました:
// class Pet
function Pet(name) {
this.getName = function() { return name }; ; };
}
Pet.prototype.toString = function() {
return "このペットの名前は次のとおりです。
}; Pet
var parrotty = new Pet(“Parrotty the Parrot”);
alert(parrotty); さて、Pet から派生したクラス Dog を作成するにはどうすればよいでしょうか。図 9 からわかるように、Dog には別のプロパティ Breeze があり、Pet の toString メソッドをオーバーライドします (JavaScript の規約では、C# で推奨されている Pascal ケーシングではなく、メソッド名とプロパティ名にキャメル ケーシングを使用することに注意してください)。図 10 は、これを行う方法を示しています。
Pet クラスからの派生
コードをコピーします
コードは次のとおりです: // class Dog : Pet // public Dog(string name, string Brede) function Dog(name, Breeze) {
// think Dog : Base(name)
Pet .call(this, name);
this.getBreed = function() { return Breed; };
// 読み取り専用です。 function(newBreed) { name = newName; };
}
// これにより、Dog.prototype は Pet.prototype
Dog.prototype = new Pet() から継承されます。 // Pet .prototype.constructor
// が Pet を指すことを覚えておいてください。Dog インスタンスの
// コンストラクターが Dog を指すようにします。 / 次に、Pet .prototype.toString
Dog.prototype.toString = function() {
return 「この犬の名前は「 this.getName()
」、その品種は「 this 」をオーバーライドします。 .getBreed();
};
// クラス Dog
var Dog = new Dog(“Buddy”, “Great Dane”); // 新しい toString() をテストします。 🎜>alert(dog );
//instanceof のテスト (is 演算子と同様)
// (dog は Dog)? はい
alert(dog instanceof Dog);ペットです)? はい
alert(犬インスタンスオブペット);
// (犬はオブジェクトです)? はい
アラート(犬インスタンスオブオブジェクト);
プロトタイプを使用しますテクニックが正しく設定されている プロトタイプ チェーンが含まれているため、C# を使用している場合、テストされたインスタンスは期待どおりに実行されます。また、特権メソッドは引き続き期待どおりに機能します。
モック名前空間
C および C# では、名前空間は名前の競合を最小限に抑えるために使用されます。たとえば、.NET Framework では、名前空間は Microsoft.Build.Task.Message クラスと System.Messaging.Message を区別するのに役立ちます。 JavaScript には名前空間をサポートする言語固有の機能はありませんが、オブジェクトを使用して名前空間をエミュレートするのは簡単です。 JavaScript ライブラリを作成する場合は、次のように、グローバル関数やクラスを定義せずに名前空間内でラップできます。
コードをコピー
コードは次のとおりです:
var MSDNMagNS = {};
MSDNMagNS.Pet = function(name) { // ここにコードを記述します }; toString = function() { // code };
var pet = new MSDNMagNS.Pet("Yammer");
名前空間のレベルは一意ではない可能性があります。ネストされた名前空間が作成されました: コード
コードをコピー
コードは次のとおりです:
var MSDNMagNS = {};
// ネストされた名前空間「Examples」
MSDNMagNS.Examples = {};
MSDNMagNS.Examples.Pet = function(name) { // コード } ;
MSDNMagNS.Examples.Pet.prototype.toString = function() { // コード };
var pet = new MSDNMagNS.Examples.Pet(“Yammer”);ご想像のとおり、このような長くネストされた名前空間を入力するのは面倒な作業です。 幸いなことに、ライブラリ ユーザーは名前空間の短いエイリアスを簡単に指定できます:
// 「Eg = MSDNMagNS.Examples; を使用する」と考えてください。
var Eg = MSDNMagNS.Examples; (“Yammer”);
alert(pet);
Microsoft AJAX ライブラリのソース コードを見ると、ライブラリの作成者が同様の手法を使用していることがわかります。名前空間を実装します (静的メソッド Type.registerNamespace の実装を参照)。詳細については、サイドバー「OOP と ASP.NET AJAX」を参照してください。
これが JavaScript の記述方法でしょうか?
JavaScript がオブジェクト指向プログラミングを非常によくサポートしていることがわかりました。これはプロトタイプベースの言語ですが、他の一般的な言語で一般的なクラスベースのプログラミング スタイルに対応できる柔軟性とパワーを備えています。しかし問題は、JavaScript コードをこのように記述すべきかということです。 JavaScript でプログラミングする方法は、C# または C でコーディングする方法と同じである必要がありますか? JavaScript にはない機能をエミュレートする、より賢い方法はあるでしょうか?すべてのプログラミング言語は異なり、ある言語にとって最適なものが、別の言語にとって最適であるとは限りません。
JavaScript では、(クラスを継承するクラスとは異なり) オブジェクトがオブジェクトを継承するのを見てきました。したがって、静的継承階層を使用して多くのクラスを構築することは、JavaScript には適さない可能性があります。おそらく、Douglas Crockford が記事「JavaScript におけるプロトタイプの継承」で述べたように、JavaScript でプログラミングする方法は、プロトタイプ オブジェクトを作成し、次の単純なオブジェクト関数を使用して、元のオブジェクトを継承する新しいオブジェクトを作成することです。 >
コードをコピー
}
その後、JavaScript のオブジェクトは簡単に作成できます。必要に応じて、オブジェクトに新しいフィールドと新しいメソッドを追加します。
これは確かに素晴らしいことですが、世界中のほとんどの開発者がクラスベースのプログラミングに精通していることは否定できません。実際、ここでもクラスベースプログラミングが登場します。 JavaScript 2.0 には、ECMA-262 仕様の次期バージョン 4 に従って実際のクラスが含まれます (ECMA-262 は JavaScript の公式仕様です)。その結果、JavaScript はクラスベースの言語に進化しています。ただし、JavaScript 2.0 が広く使用されるようになるまでには数年かかるでしょう。同時に、現在の JavaScript はプロトタイプベースのスタイルとクラスベースのスタイルで JavaScript コードを完全に読み書きできることも明確にする必要があります。
今後の展望
インタラクティブなシック クライアント AJAX アプリケーションの普及により、JavaScript は .NET 開発者にとって最も重要なツールの 1 つになりつつあります。ただし、C、C#、Visual Basic などの言語に慣れている開発者は、そのプロトタイプの性質に最初は驚くかもしれません。 JavaScript を学習した経験は、多少の不満はありましたが、豊かな経験であったことがわかりました。この記事があなたの経験を少しでもスムーズにしてくれれば幸いです。それがまさに私が目指していることだからです。