列挙可能な (enumerable)プロパティとは、内部の [[Enumerable]] フラグがtrueに設定されているものであり、単純な代入や初期化で生成されたプロパティはデフォルトでそうなります(一方、Object.defineProperty()
で追加したプロパティはデフォルトで [[Enumerable]] が false になります)。 列挙可能なプロパティは、名前がSymbol
である場合を除いて、 for...in
ループの対象になります。
直接所有の (own)プロパティとは、プロトタイプチェーン上ではなく、オブジェクトに直接属しているプロパティのことです。
プロパティはまとめて取り扱うこともでき、プロパティの検出、取得、反復/列挙 に関連する多数の組み込み機能があります。それらを下図に示します。 その下にあるサンプルコードでは、組み込み機能が対応しないカテゴリに対応する方法を示します。
プロパティの列挙可能性と直接所有 - 検出、取得 および 反復
機能 |
直接所有するオブジェクト |
直接所有するオブジェクトとプロトタイプチェーン |
プロトタイプチェーンのみ |
検出 |
|
追加のコードが必要 |
追加のコードが必要 |
取得 |
|
追加のコードが必要 |
追加のコードが必要 |
反復 |
|
列挙可能 |
列挙不可能 |
列挙可能と列挙不可能 |
for..in |
追加のコードが必要 |
追加のコードが必要 |
|
追加のコードが必要 |
列挙可能/直接所有の条件でプロパティを取得する方法
以下に示すのは全てのケースで最も効率的なアルゴリズムではなく、簡潔なデモであることに注意してください。
- 検出は下記で可能です:
SimplePropertyRetriever.使いたいgetメソッド(obj).indexOf(prop) > -1
- 反復は下記で可能です:
SimplePropertyRetriever.使いたいgetメソッド(obj).forEach(function (value, prop) {});
(または filter()
, map() 等を使う)
var SimplePropertyRetriever = {
getOwnEnumerables: function (obj) {
return this._getPropertyNames(obj, true, false, this._enumerable);
// Or could use for..in filtered with hasOwnProperty or just this: return Object.keys(obj);
},
getOwnNonenumerables: function (obj) {
return this._getPropertyNames(obj, true, false, this._notEnumerable);
},
getOwnEnumerablesAndNonenumerables: function (obj) {
return this._getPropertyNames(obj, true, false, this._enumerableAndNotEnumerable);
// Or just use: return Object.getOwnPropertyNames(obj);
},
getPrototypeEnumerables: function (obj) {
return this._getPropertyNames(obj, false, true, this._enumerable);
},
getPrototypeNonenumerables: function (obj) {
return this._getPropertyNames(obj, false, true, this._notEnumerable);
},
getPrototypeEnumerablesAndNonenumerables: function (obj) {
return this._getPropertyNames(obj, false, true, this._enumerableAndNotEnumerable);
},
getOwnAndPrototypeEnumerables: function (obj) {
return this._getPropertyNames(obj, true, true, this._enumerable);
// Or could use unfiltered for..in
},
getOwnAndPrototypeNonenumerables: function (obj) {
return this._getPropertyNames(obj, true, true, this._notEnumerable);
},
getOwnAndPrototypeEnumerablesAndNonenumerables: function (obj) {
return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable);
},
// Private static property checker callbacks
_enumerable : function (obj, prop) {
return obj.propertyIsEnumerable(prop);
},
_notEnumerable : function (obj, prop) {
return !obj.propertyIsEnumerable(prop);
},
_enumerableAndNotEnumerable : function (obj, prop) {
return true;
},
// Inspired by https://stackoverflow.com/a/8024294/271577
_getPropertyNames : function getAllPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) {
var props = [];
do {
if (iterateSelfBool) {
Object.getOwnPropertyNames(obj).forEach(function (prop) {
if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) {
props.push(prop);
}
});
}
if (!iteratePrototypeBool) {
break;
}
iterateSelfBool = true;
} while (obj = Object.getPrototypeOf(obj));
return props;
}
};
検出表
|
in |
for..in |
hasOwnProperty |
propertyIsEnumerable |
in Object.keys |
in Object.getOwnPropertyNames |
列挙可能 |
true |
true |
true |
true |
true |
true |
列挙不可能 |
true |
false |
true |
false |
false |
true |
継承, 列挙可能 |
true |
true |
false |
false |
false |
false |
継承, 列挙不可能 |
true |
false |
false |
false |
false |
false |
Symbolキーが含まれるか |
true |
false |
true |
true |
false |
false |
関連情報