JavaScript Advent Calendar 2015の19日目の記事です。
JavaScript初心者のため勉強中。
プロパティ記述子とは
ある本を読んでいたのだが、こんなようなことが書かれていた。
JavaScriptでのオブジェクトは「名前」と「値」の組み合わせである__プロパティ__の集合。(中略)厳密にはプロパティ名と属性群の組み合わせで、これらの属性のセットを__プロパティ記述子__という。
…はい?属性群?
よく分からんし、見たことない。
とりあえずgetOwnPorpertyDescription
メソッドで設定内容を参照できるらしいので確認。
var obj = {};
obj.a = 1;
Object.getOwnPropertyDescriptor(obj, "a");
// 結果
Object {
value: 1,
writable: true,
enumerable: true,
configurable: true
}
ほー、なんかvalue以外にもwritable, enumerable, configurableとか設定されとるが、わしそんなもの設定しとらんぞ?というわけで各属性の特徴を調べてみた。
DataDescriptorの各属性の説明
-
Value → 文字通り値。
-
Writable → Value属性を更新できるか否かを指定。
-
Enumerable → for-in文で補足するか否かを指定。
-
Configurable → プロパティ記述子を変更できるか否かを指定。(ただしWritable以外)
頭を大文字で記載したが、使う時は小文字で使うルールらしい。
以下、各属性の使い方。(Value属性は省く)
Writable属性
オブジェクトを定義し、value, wraitableを設定。
// オブジェクトの定義
obj = {};
// プロパティ"a"の設定
Object.defineProperty(obj, "a", {
value: 1,
writable: false
});
// プロパティ"b"の設定
Object.defineProperty(obj, "b", {
value: "hoge",
writable: true
});
console.log(obj.a);
console.log(obj.b);
// プロパティ"a"のvalueを変更
obj.a = 200;
// プロパティ"b"のvalueを変更
obj.b = "fuga";
console.log(obj.a);
console.log(obj.b);
// 結果
1
hoge
1
fuga
はい、aではvalue
が更新されないこと、bでは更新されることが確認できた。
ちなみに、この後もう一度defineProperty
メソッドからwritable
をtrue
に変更しようとしたが、エラーとなった。一度定義すると後からは変更できないらしい。
※指定しないとデフォルトでfalse
に設定される。
Enumerable属性
オブジェクトを定義し、それをfor-in文で表示してみると、なんか余計なものが表示される。
// プロパティ"hoge"の設定
Object.prototype.hoge = function() { console.log("fuga") };
// オブジェクトの定義
var obj = {};
for (var key in obj){ console.log(key) };
// 結果
hoge
undefined
ん?hoge
が表示されている。 これがいわゆる__オブジェクト汚染__というやつか。
これを回避するためにEnumerable
属性をfalse
にするといいと。
// プロパティ"hoge"の設定
Object.defineProperty(Object.prototype, "hoge",{
value: function() { console.log("fuga") },
enumerable: false
});
// オブジェクトの定義
var obj = {};
for (var key in obj) { console.log(key) };
obj.hoge();
// 結果
undefined
fuga
よし。これでfor-in
文ではundifined
が表示され、obj.hoge()
にてfuga
が表示された。
Configure属性
オブジェクトを定義し、それを更新・削除してみる。
// オブジェクトの定義
var obj = {};
// プロパティ"hoge"の設定
Object.defineProperty(Object.prototype, "hoge",{
value: 1,
enumerable: false,
configurable: false
});
// "hoge"プロパティの属性確認
Object.getOwnPropertyDescriptor(obj, "hoge");
// "hoge"プロパティの更新
Object.defineProperty(obj, "hoge", {
enumerable: true,
configurable: true,
writable: true
});
// "hoge"プロパティの属性確認
Object.getOwnPropertyDescriptor(obj, "hoge");
// "hoge"プロパティの削除
delete obj.hoge;
// "hoge"プロパティの属性確認
Object.getOwnPropertyDescriptor(obj, "hoge");
// 結果
Object {
value: 1,
writable: false,
enumerable: false,
configurable: false
}
TypeError発生
Object {
value: 1,
writable: false,
enumerable: false,
configurable: false
}
false
Object {
value: 1,
writable: false,
enumerable: false,
configurable: false
}
よし。こちらも各属性を更新しようとするとTypeErrorとなり、プロパティが削除されないことが確認できた。
※1 初期設定でconfigurableをtrueにしていた場合、変更できることも確認済み。
※2 一度falseにすると、今後更新できないことも確認済み。
終わりに
今回はデータプロパティのみ扱ったが、アクセサプロパティと言うものも存在し、そちらではGet, Set
関数を持つ。
プロパティ記述子の活用法はまだ模索段階らしく、この時はこれを使う、という体系化はなされていないそうな。まぁ使う頻度はそんなにはなさそうだが、知っていて損はないかな。
※何か間違っている記述がございましたら、ご指摘いただけますと幸いです!