プロパティの列挙可能性と所有権
列挙可能プロパティは、内部の列挙可能(enumerable)フラグが true に設定されているプロパティです。これは、単純な代入や初期化で作成されたプロパティのデフォルトです (Object.defineProperty で追加したプロパティはデフォルトで列挙可能性が false になります)。プロパティのキーが Symbol でない限り、列挙可能なプロパティは 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 http://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 |
obj.hasOwnProperty |
obj.propertyIsEnumerable |
Object.keys |
Object.getOwnPropertyNames |
Object.getOwnPropertyDescriptors |
Reflect.ownKeys() |
|
---|---|---|---|---|---|---|---|---|
列挙可能 | true | true | true | true | true | true | true | true |
列挙不可能 | true | false | true | false | false | true | true | true |
Symbols キー | true | false | true | true | false | false | true | true |
継承された列挙可能 | true | true | false | false | false | false | false | false |
継承された列挙不可能 | true | false | false | false | false | false | false | false |
継承された Symbols キー | true | false | false | false | false | false | false | false |