[解決済み] javascriptのObject.definePropertyの使い方
質問
を使用する方法を調べました。
Object.defineProperty
というメソッドがあるのですが、まともなものが見つかりませんでした。
ある人から このコードのスニペット :
Object.defineProperty(player, "health", {
get: function () {
return 10 + ( player.level * 15 );
}
})
でも、よくわからないんです。主に
get
が、理解できないのです(ダジャレ)。どのような仕組みになっているのでしょうか?
どのように解決するのですか?
という質問がありましたので 類似の質問 では、順を追って説明しましょう。少し長いですが、私がこれを書くのに費やした時間よりはるかに長い時間を節約できるかもしれません。
プロパティ は、クライアントコードをきれいに分離するために設計された OOP 機能です。例えば、あるe-shopでは、このようなオブジェクトがあるかもしれません。
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-shop)で、商品に割引を追加することができます。
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
(
アクセサー
) と
setDiscount
(
ミューテーター
) メソッドを使用します。問題は、ほとんどのメンバーが一般的な変数と同じように振る舞うことです。ただ、ここでは割引に特別な注意が必要です。しかし、良い設計では、コードの拡張性を保つために、すべてのデータメンバをカプセル化する必要があります。そのため、何もしないコードをたくさん追加する必要があります。これもまた悪いデザインで
ボイラープレート・アンチパターン
. フィールドを後でメソッドにリファクタリングできないこともあります(eshopのコードが大きくなったり、サードパーティのコードが古いバージョンに依存することもあります)ので、ここではボイラープレートはあまり悪ではありません。しかし、それでも、それは悪です。だから、多くの言語でプロパティが導入されたのだ。元のコードのまま、discount メンバをプロパティに変換して
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
最後の1行に注目してください。正しい割引額の責任は、クライアントコード(e-shopの定義)から商品定義に移されました。商品には、そのデータメンバーの一貫性を保つ責任があります。良いデザインとは、(おおざっぱに言うと)コードが私たちの考えと同じように動くことです。
プロパティの話はこれくらいにして。しかし、javascriptはC#のような純粋なオブジェクト指向言語とは異なり、機能のコーディングが異なります。
C#の場合 フィールドからプロパティへの変換は 変更 そのため、パブリックフィールドは、以下のようにコーディングする必要があります。 自動実装されるプロパティ もし、あなたのコードが別途コンパイルされたクライアントで使用される可能性がある場合。
ジャバスクリプトの場合 の場合、標準的なプロパティ(上記のゲッターとセッターを持つデータメンバ)は アクセサー記述子 (質問にあるリンクの中)。専ら、あなたは データ記述子 (を使用することはできません。 値 と セット を同じプロパティの上に置く)。
-
アクセサー記述子
= get + set (上の例参照)
- 得る は関数でなければなりません。その返り値はプロパティを読み込む際に使用されます。 未定義 これは、undefined を返す関数と同じように動作します。
- セット は関数でなければなりません; そのパラメータはプロパティに値を割り当てる際にRHSで埋められます; 指定されない場合、デフォルトは 未定義 これは空の関数と同じように動作します。
-
データ記述子
= 値 + 書き込み可能(下記例参照)
- 値 デフォルト 未定義 もし 書き込み可能 , 設定可能 と 列挙可能 (が真であれば、このプロパティは通常のデータフィールドと同様に動作します。
- 書き込み可能 - デフォルト false でない場合は 真 書き込もうとしてもエラーにならず無視されます*!
どちらの記述子もこれらのメンバを持つことができる。
- コンフィギュラブル - デフォルト 虚偽 もしtrueでなければ、そのプロパティは削除できません。削除しようとしてもエラーにならず無視されます*!
-
列挙可能
- デフォルト
false
で反復処理されます。
for(var i in theObject)
false の場合、反復処理されませんが、public としてアクセス可能です。
* でない限り ストリクトモード - で捕捉されない限り、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段階の制限でオブジェクトを制限することができます。
-
オブジェクト.preventExtensions(yourObject)
に新しいプロパティが追加されるのを防ぎます。
yourObject
. 使用方法
Object.isExtensible(<yourObject>)
を使用して、そのメソッドがオブジェクト上で使用されたかどうかをチェックします。防止策は 浅い (を読んでください(以下略)。 -
Object.seal(yourObject)です。
上記と同じで、プロパティは削除できません。
configurable: false
をすべてのプロパティに適用します)。使用方法Object.isSealed(<yourObject>)
を使用すると、オブジェクト上でこの機能を検出することができます。シールは 浅い (をお読みください)。 -
Object.freeze(yourObject)です。
上記と同じで、プロパティは変更できません。
writable: false
をデータディスクリプタを持つすべてのプロパティに適用します)。セッターの書き込み可能なプロパティは影響を受けません(プロパティがないため)。フリーズは 浅い これは、プロパティがオブジェクトの場合、そのプロパティは凍結されないことを意味します。 ディープコピー - 複製 ). 使用方法Object.isFrozen(<yourObject>)
で検出します。
ほんの数行楽しく書くだけなら、わざわざこんなことをする必要はないでしょう。しかし、もしあなたが(リンク先の質問にあるように)ゲームをコーディングしたいのであれば、良いデザインに気を配るべきです。について何かググってみてください。 アンチパターン と コード臭 . のような状況を回避するのに役立ちます。 ああ、またコードを完全に書き直さなければならない!" もし、あなたがたくさんのコードを書きたいのであれば、何ヶ月も絶望する必要はありません。幸運を祈ります。
関連
-
[解決済み] テスト
-
[解決済み] 配列から特定の項目を削除するにはどうすればよいですか?
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] JavaScriptでメールアドレスを検証するのに最適な方法は何ですか?
-
[解決済み] JavaScriptでオブジェクトをディープクローンする最も効率的な方法は何ですか?
-
[解決済み] JavaScriptでタイムスタンプを取得する方法は?
-
[解決済み] JavaScriptのオブジェクトにキーが存在するかどうかをチェックする?
-
[解決済み】オブジェクトからプロパティを削除する(JavaScript)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Facebook Graph API のクエリで with=location を使用すると "Uncaught (in promise) undefined" というエラーが発生する。
-
[解決済み】 Uncaught TypeError: data.push is not a function
-
[解決済み】「Uncaught TypeError: Chromeで "Illegal invocation "が発生する。
-
[解決済み] React with ES7: Uncaught TypeError: Cannot read property 'state' of undefined [duplicate] (未定義のプロパティ'state'を読み込むことはできません。
-
[解決済み】別のjsファイル内でJavaScriptの関数を呼び出す
-
[解決済み】WebpackとBabelで「このファイルタイプを扱うには適切なローダーが必要な場合があります。
-
[解決済み】React-Routerの子が1つしかない。
-
[解決済み】Vueのテンプレートまたはレンダー関数が定義されていない 私はどちらも使っていないのですが?
-
[解決済み】TypeError: AngularJSで未定義のプロパティ'get'を読み取れない
-
[解決済み】 \u003C とは何ですか?