ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScriptのデザインパターン(カプセル化)_JavaScriptスキルを学ぶ
JavaScript では、抽象クラスとインターフェイスはサポートされていません。 JavaScript 自体も弱い型付け言語です。 JavaScript には、型をカプセル化する機能がありませんし、それ以上のことを行う必要もありません。 JavaScript でデザインパターンを実装する場合、型を区別しないことは不名誉とも言えますし、救いとも言えます。
デザインパターンの観点から見ると、パッケージングはより重要なレベルでパッケージングの変更に反映されます。
変更をカプセル化することにより、システムの進化中に、システムの安定した部分と変更されやすい部分が分離されます。これらの部分がカプセル化されている場合は、それらの部分を置き換えるだけで済みます。はい、交換は比較的簡単です。これにより、プログラムの安定性と拡張性を最大限に確保できます。
JavaScript カプセル化には 3 つの基本モードがあります:
1. 合意優先の原則を使用し、すべてのプライベート変数は _
で始まります。<script type="text/javascript"> /** * 使用约定优先的原则,把所有的私有变量都使用_开头 */ var Person = function (no, name, age) { this.setNo(no); this.setName(name); this.setAge(age); } Person.prototype = { constructor: Person, checkNo: function (no) { if (!no.constructor == "string" || no.length != 4) throw new Error("学号必须为4位"); }, setNo: function (no) { this.checkNo(no); this._no = no; }, getNo: function () { return this._no; setName: function (name) { this._name = name; }, getName: function () { return this._name; }, setAge: function (age) { this._age = age; }, getAge: function () { return this._age; }, toString: function () { return "no = " + this._no + " , name = " + this._name + " , age = " + this._age; } }; var p1 = new Person("0001", "小平果", "22"); console.log(p1.toString()); //no = 0001 , name = 小平果 , age = 22 p1.setNo("0003"); console.log(p1.toString()); //no = 0003 , name = 小平果 , age = 22 p1.no = "0004"; p1._no = "0004"; console.log(p1.toString()); //no = 0004 , name =小平果 , age = 22 </script>
コードを読んだ後、_ で始まる変数をすべて入れても、そのままアクセスできます。これはカプセル化と言えますか?が優先されます。
このアンダースコアの使用はよく知られた命名規則であり、プロパティがオブジェクトの内部使用のみを目的としており、プロパティにアクセスしたり直接設定したりすると、予期しない結果が生じる可能性があることを示します。これにより、プログラマが誤って使用することを防ぐことができますが、意図的に使用することは防止できません。
このメソッドは、少なくともメンバー変数の getter メソッドと setter メソッドがオブジェクト内ではなくプロトタイプ内にあるため、全体的には良い選択です。これは不可能であり、カプセル化を厳密に実装する必要があると思われる場合は、2 番目の方法を検討してください。
2. カプセル化を厳密に実装する
<script type="text/javascript"> /** * 使用这种方式虽然可以严格实现封装,但是带来的问题是get和set方法都不能存储在prototype中,都是存储在对象中的 * 这样无形中就增加了开销 */ var Person = function (no, name, age) { var _no , _name, _age ; var checkNo = function (no) { if (!no.constructor == "string" || no.length != 4) throw new Error("学号必须为4位"); }; this.setNo = function (no) { checkNo(no); _no = no; }; this.getNo = function () { return _no; } this.setName = function (name) { _name = name; } this.getName = function () { return _name; } this.setAge = function (age) { _age = age; } this. getAge = function () { return _age; } this.setNo(no); this.setName(name); this.setAge(age); } Person.prototype = { constructor: Person, toString: function () { return "no = " + this.getNo() + " , name = " + this.getName() + " , age = " + this.getAge(); } } ; var p1 = new Person("0001", "小平果", "22"); console.log(p1.toString()); //no = 0001 , name =小平果 , age = 22 p1.setNo("0003"); console.log(p1.toString()); //no = 0003 , name = 小平果 , age = 22 p1.no = "0004"; console.log(p1.toString()); //no = 0003 , name = 小平果 , age = 22 </script>
これは、これまでに説明した他のオブジェクト作成モードとどのように違うのでしょうか? 上記の例では、オブジェクトのプロパティを作成および参照するときに常に this キーワードを使用します。この例では、これらの変数を var で宣言します。これは、それらが Person コンストラクター内にのみ存在することを意味します。 checkno 関数も同様に宣言されるため、プライベート メソッドになります。
これらの変数や関数にアクセスする必要があるメソッドは、Personal で宣言するだけで済みます。これらのメソッドはパブリックでありながらプライベートなプロパティおよびメソッドにアクセスできるため、特権メソッドと呼ばれます。これらの特権関数にオブジェクトの外部からアクセスするには、先頭にキーワード this を付けます。これらのメソッドは Person コンストラクターのスコープ内で定義されているため、プライベート プロパティにアクセスできます。これらのプロパティはパブリックではないため、this キーワードを使用せずに参照されます。すべての getter メソッドと assigner メソッドは、これを使用せずにこれらのプロパティを直接参照するように変更されました。
プライベート プロパティへの直接アクセスを必要としないメソッドは、元どおり Person.prototype で宣言できます。 toString() メソッドと同様です。プライベート メンバーへの直接アクセスを必要とするメソッドのみを特権メソッドとして設計する必要があります。ただし、各オブジェクト インスタンスにはすべての特権メソッドの新しいコピーが含まれるため、特権メソッドが多すぎるとメモリが大量に消費されます。
上記のコードを見ると、this. 属性名が削除され、カプセル化が厳密に実装されています。ただし、すべてのメソッドがオブジェクト内に存在します。これによりメモリのオーバーヘッドが増加します。
3. クロージャ
にカプセル化されます。<script type="text/javascript"> var Person = (function () { //静态方法(共享方法) var checkNo = function (no) { if (!no.constructor == "string" || no.length != 4) throw new Error("学号必须为4位"); }; //静态变量(共享变量) var times = 0; //return the constructor. return function (no, name, age) { console.log(times++); // 0 ,1 , 2 var no , name , age; //私有变量 this.setNo = function (no) //私有方法 { checkNo(no); this._no = no; }; this.getNo = function () { return this._no; } this.setName = function (name) { this._name = name; } this.getName = function () { return this._name; } this.setAge = function (age) { this._age = age; } this.getAge = function () { return this._age; } this.setNo(no); this.setName(name); this.setAge(age); } })(); Person.prototype = { constructor: Person, toString: function () { return "no = " + this._no + " , name = " + this._name + " , age = " + this._age; } }; var p1 = new Person("0001", "小平果", "22"); var p2 = new Person("0002", "abc", "23"); var p3 = new Person("0003", "aobama", "24"); console.log(p1.toString()); //no = 0001 , name = 小平果 , age = 22 console.log(p2.toString()); //no = 0002 , name = abc , age = 23 console.log(p3.toString()); //no = 0003 , name = aobama , age = 24 </script>
上記のコードは、JS エンジンが読み込まれた後、 Person = 即時実行関数を直接実行します。その後、この関数はサブ関数を返します。これは、新しい Person によって呼び出されるコンストラクターであり、サブ関数は次のことを維持します。即時実行関数内の checkNo(no) と time への参照 (明らかなクロージャー)。したがって、checkNo と time は、3 つのオブジェクトを作成した後、それぞれ 0、1、2 になります。このアプローチの利点は、Personal で再利用する必要があるメソッドとプロパティをプライベートにして、オブジェクト間で共有できることです。
ここでの プライベート メンバー と 特権メンバー は、コンストラクター内で宣言されたままです。ただし、コンストラクターは通常の関数からインライン関数に変更され、それを含む関数の戻り値として変数 person に渡されます。これにより、静的プライベート メンバーを宣言できるクロージャが作成されます。外側の関数宣言の後にある空の括弧のペアは、コードがロードされるとすぐに関数を実行するために非常に重要です。この関数の戻り値は別の関数であり、それが Person 変数に代入されるため、Person はコンストラクターになります。この内部関数は、Person をインスタンス化するときに呼び出されます。外部関数は、静的メンバーを格納するために使用できるクロージャーを作成するためにのみ使用されます。
この例では、 Person のインスタンスごとにこのメソッドの新しいコピーを生成するのは意味がないため、 checkno は静的メソッドになるように設計されています。また、静的属性 time もあり、その関数 は、Person コンストラクター への呼び出しの合計数を追跡することです。
以上がこの記事の全内容です。カプセル化の意味をさらに学ぶのに役立つことを願っています。