ご存知のとおり、オブジェクトはプロパティを保存できます。
これまで、プロパティは私たちにとって単純な「キーと値」のペアでした。しかし、オブジェクト プロパティは実際には、より柔軟で強力なものです。
この章では、追加の構成オプションを学習し、次の章では、それらを目に見えない形で getter/setter 関数に変換する方法を説明します。
オブジェクトのプロパティには、 value
ほかに 3 つの特別な属性 (いわゆる「フラグ」) があります。
writable
– true
の場合は値を変更できます。それ以外の場合は読み取り専用です。
enumerable
– true
の場合はループ内にリストされ、それ以外の場合はリストされません。
configurable
– true
の場合、プロパティを削除してこれらの属性を変更できます。それ以外の場合は変更できません。
通常、彼らは現れないので、私たちはまだ彼らを見ていません。 「通常の方法」でプロパティを作成すると、それらはすべてtrue
なります。ただし、いつでも変更することができます。
まず、これらのフラグを取得する方法を見てみましょう。
メソッド Object.getOwnPropertyDescriptor を使用すると、プロパティに関する完全な情報をクエリできます。
構文は次のとおりです。
let 記述子 = Object.getOwnPropertyDescriptor(obj, propertyName);
obj
情報を取得するオブジェクト。
propertyName
プロパティの名前。
返される値は、いわゆる「プロパティ記述子」オブジェクトです。これには、値とすべてのフラグが含まれます。
例えば:
ユーザー = { にします 名前:「ジョン」 }; let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); alert( JSON.stringify(記述子, null, 2 ) ); /* プロパティ記述子: { "値": "ジョン", 「書き込み可能」: true、 "列挙可能": true、 「構成可能」: true } */
フラグを変更するには、Object.defineProperty を使用します。
構文は次のとおりです。
Object.defineProperty(obj, propertyName, 記述子)
obj
、 propertyName
記述子を適用するオブジェクトとそのプロパティ。
descriptor
適用するプロパティ記述子オブジェクト。
プロパティが存在する場合、 defineProperty
そのフラグを更新します。それ以外の場合は、指定された値とフラグを使用してプロパティを作成します。この場合、フラグが指定されていない場合は、 false
みなされます。
たとえば、ここではすべての false フラグを使用してプロパティname
が作成されます。
ユーザー = {} にします。 Object.defineProperty(ユーザー, "名前", { 値: 「ジョン」 }); let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); alert( JSON.stringify(記述子, null, 2 ) ); /* { "値": "ジョン", 「書き込み可能」: false、 "列挙可能": false、 「構成可能」: false } */
上記の「通常に作成された」 user.name
と比較してください。現在、すべてのフラグが false になっています。それが望ましくない場合は、 descriptor
でtrue
に設定する方がよいでしょう。
次に、例によってフラグの効果を見てみましょう。
writable
フラグを変更して、 user.name
書き込み不可 (再割り当てできない) にしましょう。
ユーザー = { にします 名前:「ジョン」 }; Object.defineProperty(ユーザー, "名前", { 書き込み可能: false }); user.name = "ピート"; // エラー: 読み取り専用プロパティ 'name' に割り当てることはできません
これで、独自のdefineProperty
を適用して私たちの名前をオーバーライドしない限り、誰もユーザーの名前を変更できなくなります。
エラーは厳密モードでのみ表示されます
非厳密モードでは、書き込み不可能なプロパティなどへの書き込み時にエラーは発生しません。しかし、作戦はまだ成功しません。フラグに違反するアクションは、非厳密では黙って無視されます。
以下は同じ例ですが、プロパティは最初から作成されています。
ユーザー = { }; Object.defineProperty(ユーザー, "名前", { 値: "ジョン"、 // 新しいプロパティについては、何が真実かを明示的にリストする必要があります 列挙可能: true、 構成可能: true }); アラート(ユーザー名); // ジョン user.name = "ピート"; // エラー
次に、カスタムtoString
user
に追加しましょう。
通常、オブジェクトの組み込みtoString
列挙不可能であり、 for..in
には表示されません。しかし、独自のtoString
追加すると、デフォルトでは次のようにfor..in
に表示されます。
ユーザー = { にします 名前:「ジョン」、 toString() { this.name を返します。 } }; // デフォルトでは、両方のプロパティがリストされます。 for (ユーザーにキーを入力させます)alert(key); // 名前、toString
それが気に入らない場合は、 enumerable:false
を設定できます。そうすれば、組み込みのものと同じように、 for..in
ループには表示されません。
ユーザー = { にします 名前:「ジョン」、 toString() { this.name を返します。 } }; Object.defineProperty(user, "toString", { 列挙可能: false }); // これで toString が消えます。 for (ユーザーにキーを入力させます)alert(key); // 名前
列挙不可能なプロパティもObject.keys
から除外されます。
アラート(オブジェクト.キー(ユーザー)); // 名前
構成不可能なフラグ ( configurable:false
) が、組み込みオブジェクトおよびプロパティに事前設定されている場合があります。
構成不可能なプロパティは削除できず、その属性は変更できません。
たとえば、 Math.PI
書き込み不可、列挙不可、構成不可です。
let 記述子 = Object.getOwnPropertyDescriptor(Math, 'PI'); alert( JSON.stringify(記述子, null, 2 ) ); /* { 「値」: 3.141592653589793、 「書き込み可能」: false、 "列挙可能": false、 「構成可能」: false } */
したがって、プログラマはMath.PI
の値を変更したり、上書きしたりすることはできません。
数学.PI = 3; // 書き込み可能であるためエラー: false // Math.PI の削除も機能しません
また、 Math.PI
再度writable
可能に変更することもできません。
// 設定可能のためエラー: false Object.defineProperty(Math, "PI", { 書き込み可能: true });
Math.PI
でできることはまったくありません。
プロパティを構成不可にすることは一方通行です。 defineProperty
を使用してそれを元に戻すことはできません。
注: configurable: false
プロパティ フラグの変更とその削除を禁止しますが、その値の変更は許可します。
ここでuser.name
構成できませんが、(書き込み可能であるため) 変更することはできます。
ユーザー = { にします 名前:「ジョン」 }; Object.defineProperty(ユーザー, "名前", { 設定可能: false }); user.name = "ピート"; // 正常に動作します ユーザー名を削除します。 // エラー
そしてここでは、組み込みのMath.PI
と同じように、 user.name
「永久に封印された」定数にします。
ユーザー = { にします 名前:「ジョン」 }; Object.defineProperty(ユーザー, "名前", { 書き込み可能: false、 設定可能: false }); // user.name またはそのフラグは変更できません // これはすべて機能しません: user.name = "ピート"; ユーザー名を削除します。 Object.defineProperty(ユーザー, "名前", { 値: "ピート" });
可能な唯一の属性変更: 書き込み可能 true → false
フラグの変更に関しては小さな例外があります。
構成不可能なプロパティのwritable: true
false
に変更することで、その値の変更を防ぐことができます (別の保護層を追加するため)。ただし、その逆ではありません。
一度に多くのプロパティを定義できるメソッド Object.defineProperties(obj, descriptors) があります。
構文は次のとおりです。
Object.defineProperties(obj, { prop1: 記述子 1、 prop2: 記述子2 // ... });
例えば:
Object.defineProperties(user, { 名前: { 値: "ジョン"、書き込み可能: false }、 姓: { 値: "スミス"、書き込み可能: false }、 // ... });
したがって、一度に多くのプロパティを設定できます。
すべてのプロパティ記述子を一度に取得するには、メソッド Object.getOwnPropertyDescriptors(obj) を使用できます。
Object.defineProperties
と併用すると、「フラグを認識した」オブジェクトのクローン作成方法として使用できます。
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
通常、オブジェクトのクローンを作成するときは、次のように割り当てを使用してプロパティをコピーします。
for (ユーザーにキーを入力させます) { クローン[キー] = ユーザー[キー] }
…しかし、それはフラグをコピーしません。したがって、「より良い」クローンが必要な場合は、 Object.defineProperties
が推奨されます。
もう 1 つの違いは、 for..in
シンボリック プロパティと列挙不可能なプロパティを無視しますが、 Object.getOwnPropertyDescriptors
シンボリック プロパティと列挙不可能なプロパティを含むすべてのプロパティ記述子を返すことです。
プロパティ記述子は、個々のプロパティのレベルで機能します。
オブジェクト全体へのアクセスを制限するメソッドもあります。
Object.preventExtensions(obj)
オブジェクトへの新しいプロパティの追加を禁止します。
オブジェクト.シール(obj)
プロパティの追加/削除を禁止します。既存のすべてのプロパティに対してconfigurable: false
を設定します。
オブジェクト.フリーズ(obj)
プロパティの追加/削除/変更を禁止します。すべての既存のプロパティに対してconfigurable: false, writable: false
を設定します。
また、それらのためのテストもあります。
Object.isExtensible(obj)
プロパティの追加が禁止されている場合はfalse
を返し、そうでない場合はtrue
返します。
Object.isSealed(obj)
プロパティの追加/削除が禁止されており、既存のすべてのプロパティが構成可能である場合はtrue
を返しますconfigurable: false
。
Object.isFrozen(obj)
プロパティの追加/削除/変更が禁止されており、現在のすべてのプロパティがconfigurable: false, writable: false
の場合はtrue
を返します。
これらの方法は実際にはほとんど使用されません。