同様の質問をしたので、順を追って説明しましょう。少し長くなりますが、これを書くのに費やした時間よりもはるかに多くの時間を節約できます:
プロパティは、クライアント コードを明確に分離するために設計された OOP 機能です。たとえば、一部の e ショップでは、次のようなオブジェクトがある場合があります。
function Product(name,price) {
this.name = name;
this.price = price;
this.discount = 0;
}
var sneakers = new Product("Sneakers",20); // {name:"Sneakers",price:20,discount:0}
var tshirt = new Product("T-shirt",10); // {name:"T-shirt",price:10,discount:0}
次に、クライアント コード (e ショップ) で、製品に割引を追加できます。
function badProduct(obj) { obj.discount+= 20; ... }
function generalDiscount(obj) { obj.discount+= 10; ... }
function distributorDiscount(obj) { obj.discount+= 15; ... }
後で、e ショップの所有者は、割引が 80% を超えることはできないことに気付くかもしれません。ここで、クライアント コード内の割引の変更をすべて見つけて、行を追加する必要があります。
if(obj.discount>80) obj.discount = 80;
次に、e ショップのオーナーは、 「顧客が再販業者の場合、最大割引は 90% になる可能性がある」など、戦略をさらに変更する可能性があります。また、複数の場所で再度変更を行う必要があり、戦略が変更されるたびにこれらの行を変更することを忘れないでください。これは悪い設計です。そのため、カプセル化が OOP の基本原則です。コンストラクターが次のような場合:
function Product(name,price) {
var _name=name, _price=price, _discount=0;
this.getName = function() { return _name; }
this.setName = function(value) { _name = value; }
this.getPrice = function() { return _price; }
this.setPrice = function(value) { _price = value; }
this.getDiscount = function() { return _discount; }
this.setDiscount = function(value) { _discount = value; }
}
getDiscount
次に、 ( accessor ) およびsetDiscount
( mutator ) メソッドを変更するだけです。問題は、ほとんどのメンバーが一般的な変数のように振る舞うことです。ここでは割引だけに特別な注意が必要です。しかし、優れた設計では、コードを拡張可能に保つために、すべてのデータ メンバーをカプセル化する必要があります。そのため、何もしないコードをたくさん追加する必要があります。これも悪い設計であり、ボイラープレートのアンチパターンです。後でフィールドをメソッドにリファクタリングできない場合があるため (eshop コードが大きくなったり、一部のサードパーティ コードが古いバージョンに依存する場合があります)、ここではボイラープレートの悪さが軽減されます。しかし、それでも、それは悪です。プロパティが多くの言語に導入されたのはそのためです。元のコードを保持して、割引メンバーをプロパティに変換するだけですget
およびset
ブロック:
function Product(name,price) {
this.name = name;
this.price = price;
//this.discount = 0; // <- remove this line and refactor with the code below
var _discount; // private member
Object.defineProperty(this,"discount",{
get: function() { return _discount; },
set: function(value) { _discount = value; if(_discount>80) _discount = 80; }
});
}
// the client code
var sneakers = new Product("Sneakers",20);
sneakers.discount = 50; // 50, setter is called
sneakers.discount+= 20; // 70, setter is called
sneakers.discount+= 20; // 80, not 90!
alert(sneakers.discount); // getter is called
最後から 2 行目に注意してください。正しい割引値の責任は、クライアント コード (e ショップの定義) から製品の定義に移されました。製品は、データ メンバーの一貫性を維持する責任があります。良いデザインとは、コードが私たちの考えと同じように機能する場合です。
プロパティについてはこれで終わりです。しかし、javascript は C# のような純粋なオブジェクト指向言語とは異なり、機能のコーディングも異なります。
C#では、フィールドをプロパティに変換することは重大な変更であるため、コードが個別にコンパイルされたクライアントで使用される可能性がある場合は、パブリック フィールドを自動実装プロパティとしてコーディングする必要があります。
Javascriptでは、標準プロパティ (上記の getter と setter を持つデータ メンバー) は、アクセサ記述子(質問のリンク内) によって定義されます。排他的に、データ記述子を使用できます (同じプロパティでie valueとsetを使用することはできません):
- アクセサ記述子= get + set (上記の例を参照)
- getは関数でなければなりません。その戻り値はプロパティの読み取りに使用されます。指定されていない場合、デフォルトはundefinedであり、 undefined を返す関数のように動作します
- setは関数でなければなりません。そのパラメーターは、プロパティに値を割り当てる際に RHS で埋められます。指定されていない場合、デフォルトはundefinedであり、空の関数のように動作します
- データ記述子= 値 + 書き込み可能 (以下の例を参照)
- 値のデフォルトは未定義です。writable、 configurable、およびenumerable (下記参照) が true の場合、プロパティは通常のデータ フィールドのように動作します。
- 書き込み可能- デフォルトfalse ; trueでない、プロパティは読み取り専用です。書き込みの試みはエラーなしで無視されます*!
どちらの記述子も、次のメンバーを持つことができます。
- 構成可能- デフォルトfalse ; true でない場合、プロパティは削除できません。削除しようとしても、エラーなしで無視されます*!
- 列挙可能- デフォルトfalse ; true の場合、で反復され
for(var i in theObject)
ます。false の場合、反復されませんが、パブリックとして引き続きアクセスできます
* strict モードでない限り - その場合、 try-catch ブロックでキャッチされない限り、JS は TypeError で実行を停止します
これらの設定を読み取るには、 を使用しますObject.getOwnPropertyDescriptor()
。
例で学ぶ:
var o = {};
Object.defineProperty(o,"test",{
value: "a",
configurable: true
});
console.log(Object.getOwnPropertyDescriptor(o,"test")); // check the settings
for(var i in o) console.log(o[i]); // nothing, o.test is not enumerable
console.log(o.test); // "a"
o.test = "b"; // o.test is still "a", (is not writable, no error)
delete(o.test); // bye bye, o.test (was configurable)
o.test = "b"; // o.test is "b"
for(var i in o) console.log(o[i]); // "b", default fields are enumerable
クライアント コードにそのようなチートを許可したくない場合は、オブジェクトを 3 つの制限レベルで制限できます。
- Object.preventExtensions(yourObject)は、新しいプロパティがyourObjectに追加されるのを防ぎます。メソッドがオブジェクトで使用
Object.isExtensible(<yourObject>)
されたかどうかを確認するために使用します。予防は浅いです(以下をお読みください)。
- Object.seal(yourObject)上記と同じで、プロパティは削除できません (実質的
configurable: false
にすべてのプロパティに設定されます)。Object.isSealed(<yourObject>)
オブジェクトのこの特徴を検出するために使用シールは浅いです(以下をお読みください)。
- Object.freeze(yourObject)は上記と同じで、プロパティは変更できません (
writable: false
データ記述子を使用してすべてのプロパティに効果的に設定されます)。セッターの書き込み可能なプロパティは影響を受けません (プロパティがないため)。フリーズは浅い: プロパティが Object の場合、そのプロパティはフリーズされていないことを意味します (必要に応じて、ディープ コピー - クローニングと同様に、「ディープ フリーズ」などを実行する必要があります)。Object.isFrozen(<yourObject>)
検出するために使用
ほんの数行の楽しみを書くだけなら、これを気にする必要はありません。ただし、ゲームをコーディングしたい場合 (リンクされた質問で述べたように)、優れた設計に注意する必要があります。アンチパターンとコードの匂いについてググってみてください。「ああ、もう一度コードを完全に書き直さなければならない」というような状況を回避するのに役立ちます。、たくさんコーディングしたい場合は、何ヶ月もの絶望を救うことができます. 幸運を。