ご存知のように、オブジェクトはプロパティを格納できます。
これまで、プロパティは私たちにとって単純な「キーと値」のペアでした。しかし、オブジェクトのプロパティは実際にはより柔軟で強力なものです。
この章では追加の設定オプションについて学び、次の章ではそれらを不可視的にゲッター/セッター関数に変換する方法を見ていきます。
プロパティフラグ
オブジェクトのプロパティは、value
に加えて、3つの特別な属性(いわゆる「フラグ」)を持っています。
writable
–true
の場合、値を変更できます。そうでない場合は読み取り専用です。enumerable
–true
の場合、ループにリストされます。そうでない場合はリストされません。configurable
–true
の場合、プロパティを削除でき、これらの属性を変更できます。そうでない場合はできません。
私たちはまだそれらを見ていませんでした。なぜなら、一般的にそれらは表示されないからです。「通常の方法」でプロパティを作成すると、すべてがtrue
になります。しかし、いつでも変更することもできます。
まず、これらのフラグを取得する方法を見てみましょう。
Object.getOwnPropertyDescriptorメソッドを使用すると、プロパティに関する完全な情報を照会できます。
構文は次のとおりです。
let
descriptor =
Object.
getOwnPropertyDescriptor
(
obj,
propertyName)
;
obj
- 情報を取得するオブジェクト。
propertyName
- プロパティの名前。
返される値はいわゆる「プロパティディスクリプタ」オブジェクトです。値とすべてのフラグが含まれています。
例えば
let
user =
{
name:
"John"
}
;
let
descriptor =
Object.
getOwnPropertyDescriptor
(
user,
'name'
)
;
alert
(
JSON
.
stringify
(
descriptor,
null
,
2
)
)
;
/* property descriptor:
{
"value": "John",
"writable": true,
"enumerable": true,
"configurable": true
}
*/
フラグを変更するには、Object.definePropertyを使用できます。
構文は次のとおりです。
Object.
defineProperty
(
obj,
propertyName,
descriptor)
obj
、propertyName
- ディスクリプタを適用するオブジェクトとそのプロパティ。
descriptor
- 適用するプロパティディスクリプタオブジェクト。
プロパティが存在する場合、`defineProperty`はそのフラグを更新します. そうでない場合、指定された値とフラグでプロパティを作成します。その場合、フラグが指定されていない場合は、`false`とみなされます.
たとえば、ここでは、すべてのフラグが偽であるプロパティ`name`が作成されます
let
user =
{
}
;
Object.
defineProperty
(
user,
"name"
,
{
value:
"John"
}
)
;
let
descriptor =
Object.
getOwnPropertyDescriptor
(
user,
'name'
)
;
alert
(
JSON
.
stringify
(
descriptor,
null
,
2
)
)
;
/*
{
"value": "John",
"writable": false,
"enumerable": false,
"configurable": false
}
*/
上記の「通常作成された」`user.name`と比較してください。すべてのフラグが偽です。それが望まないものであれば、`descriptor`で`true`に設定する方が良いでしょう。
それでは、フラグの効果を例で見てみましょう。
書き込み不可
`writable`フラグを変更して、`user.name`を書き込み不可(再割り当て不可)にしましょう
let
user =
{
name:
"John"
}
;
Object.
defineProperty
(
user,
"name"
,
{
writable:
false
}
)
;
user.
name =
"Pete"
;
// Error: Cannot assign to read only property 'name'
これで、独自の`defineProperty`を適用して上書きしない限り、誰もユーザーの名前を変更できません.
非厳格モードでは、書き込み不可のプロパティなどに書き込んでもエラーは発生しません。しかし、操作は依然として成功しません。フラグに違反するアクションは、非厳格モードでは単に無視されます。
これは同じ例ですが、プロパティは最初から作成されています
let
user =
{
}
;
Object.
defineProperty
(
user,
"name"
,
{
value:
"John"
,
// for new properties we need to explicitly list what's true
enumerable:
true
,
configurable:
true
}
)
;
alert
(
user.
name)
;
// John
user.
name =
"Pete"
;
// Error
列挙不可
それでは、`user`にカスタム`toString`を追加しましょう。
通常、オブジェクトの組み込み`toString`は列挙不可であり、`for..in`には表示されません。しかし、独自の`toString`を追加すると、デフォルトでは次のように`for..in`に表示されます
let
user =
{
name:
"John"
,
toString
(
)
{
return
this
.
name;
}
}
;
// By default, both our properties are listed:
for
(
let
key in
user)
alert
(
key)
;
// name, toString
それが気に入らない場合は、`enumerable:false`を設定できます。そうすれば、組み込みのものと同様に、`for..in`ループには表示されません
let
user =
{
name:
"John"
,
toString
(
)
{
return
this
.
name;
}
}
;
Object.
defineProperty
(
user,
"toString"
,
{
enumerable:
false
}
)
;
// Now our toString disappears:
for
(
let
key in
user)
alert
(
key)
;
// name
列挙不可のプロパティも`Object.keys`から除外されます
alert
(
Object.
keys
(
user)
)
;
// name
設定不可
設定不可フラグ(`configurable:false`)は、組み込みオブジェクトとプロパティに対して事前に設定されている場合があります。
設定不可のプロパティは削除できず、その属性を変更できません。
たとえば、`Math.PI`は書き込み不可、列挙不可、設定不可です
let
descriptor =
Object.
getOwnPropertyDescriptor
(
Math,
'PI'
)
;
alert
(
JSON
.
stringify
(
descriptor,
null
,
2
)
)
;
/*
{
"value": 3.141592653589793,
"writable": false,
"enumerable": false,
"configurable": false
}
*/
そのため、プログラマーは`Math.PI`の値を変更したり、上書きしたりできません。
Math.
PI
=
3
;
// Error, because it has writable: false
// delete Math.PI won't work either
また、`Math.PI`を再び`writable`に変更することもできません
// Error, because of configurable: false
Object.
defineProperty
(
Math,
"PI"
,
{
writable:
true
}
)
;
`Math.PI`に対してできることは何もありません。
プロパティを設定不可にすることは一方通行です。`defineProperty`で元に戻すことはできません。
注意:`configurable: false`はプロパティフラグの変更と削除を防止しますが、値の変更は許可します.
ここでは`user.name`は設定不可ですが、それでも変更できます(書き込み可能なので)
let
user =
{
name:
"John"
}
;
Object.
defineProperty
(
user,
"name"
,
{
configurable:
false
}
)
;
user.
name =
"Pete"
;
// works fine
delete
user.
name;
// Error
そしてここでは、組み込みの`Math.PI`のように、`user.name`を「永遠に封印された」定数にします
let
user =
{
name:
"John"
}
;
Object.
defineProperty
(
user,
"name"
,
{
writable:
false
,
configurable:
false
}
)
;
// won't be able to change user.name or its flags
// all this won't work:
user.
name =
"Pete"
;
delete
user.
name;
Object.
defineProperty
(
user,
"name"
,
{
value:
"Pete"
}
)
;
フラグの変更について、小さな例外があります.
設定不可のプロパティに対して`writable: true`を`false`に変更して、値の変更を防止できます(別の保護層を追加するため)。ただし、逆はできません。
Object.defineProperties
多くのプロパティを一度に定義できるメソッドObject.defineProperties(obj, descriptors)があります。
構文は次のとおりです。
Object.
defineProperties
(
obj,
{
prop1:
descriptor1,
prop2:
descriptor2
// ...
}
)
;
例えば
Object.
defineProperties
(
user,
{
name:
{
value:
"John"
,
writable:
false
}
,
surname:
{
value:
"Smith"
,
writable:
false
}
,
// ...
}
)
;
そのため、多くのプロパティを一度に設定できます。
Object.getOwnPropertyDescriptors
すべてのプロパティディスクリプタを一度に取得するには、Object.getOwnPropertyDescriptors(obj)メソッドを使用できます。
`Object.defineProperties`と一緒に使用すると、オブジェクトをクローンする「フラグ対応」の方法として使用できます
let
clone =
Object.
defineProperties
(
{
}
,
Object.
getOwnPropertyDescriptors
(
obj)
)
;
通常、オブジェクトをクローンするときは、次のように代入を使用してプロパティをコピーします
for
(
let
key in
user)
{
clone[
key]
=
user[
key]
}
…しかし、これはフラグをコピーしません。「より良い」クローンが必要な場合は、`Object.defineProperties`が推奨されます.
もう1つの違いは、`for..in`はシンボリックプロパティと列挙不可プロパティを無視しますが、`Object.getOwnPropertyDescriptors`はシンボリックプロパティと列挙不可プロパティを含む*すべて*のプロパティディスクリプタを返します.
オブジェクトをグローバルに封印する
プロパティディスクリプタは、個々のプロパティのレベルで機能します.
*オブジェクト全体*へのアクセスを制限するメソッドもあります
- Object.preventExtensions(obj)
- オブジェクトに新しいプロパティを追加することを禁止します.
- Object.seal(obj)
- プロパティの追加/削除を禁止します. 既存のプロパティすべてに`configurable: false`を設定します.
- Object.freeze(obj)
- プロパティの追加/削除/変更を禁止します. 既存のプロパティすべてに`configurable: false, writable: false`を設定します.
また、それらのテストもあります
- Object.isExtensible(obj)
- プロパティの追加が禁止されている場合は`false`を、そうでない場合は`true`を返します.
- Object.isSealed(obj)
- プロパティの追加/削除が禁止されており、既存のプロパティすべてに`configurable: false`が設定されている場合は`true`を返します.
- Object.isFrozen(obj)
- プロパティの追加/削除/変更が禁止されており、現在のプロパティすべてに`configurable: false, writable: false`が設定されている場合は`true`を返します.
これらのメソッドは実際にはめったに使用されません.