オブジェクトの列挙不可能な継承されたプロパティ名を取得することは可能ですか?


99

JavaScriptでは、取得する内容に応じて、オブジェクトのプロパティを取得する方法がいくつかあります。

1)Object.keys()、オブジェクトのすべての独自の列挙可能なプロパティ、ECMA5メソッドを返します。

2)for...inループ。オブジェクトの列挙可能なすべてのプロパティを返します。それらが独自のプロパティであるか、プロトタイプチェーンから継承されているかは関係ありません。

3)Object.getOwnPropertyNames(obj)列挙可能かどうかにかかわらず、オブジェクトのすべての独自のプロパティを返します。

hasOwnProperty(prop)プロパティが継承されているかどうか、または実際にそのオブジェクトに属しているかどうかを確認できるメソッドもありpropertyIsEnumerable(prop)、名前が示すように、プロパティが列挙可能かどうかを確認できます。

これらすべてのオプションを使用すると、オブジェクトの列挙不可能な、所有されていないプロパティを取得する方法はありません。これは、私がやりたいことです。これを行う方法はありますか?言い換えれば、継承された列挙できないプロパティのリストをどうにかして取得できますか?

ありがとうございました。


4
あなたの質問は、私が尋ねようとしていた質問に答えました:列挙できないプロパティを検査する方法(事前定義されたオブジェクトで利用可能なものを調べるためだけです)。最後に、getOwnPropertyNamesを見つけました!:-)
marcus

1
@marcus :-)それがSOのすべてです。
dkugappi

回答:


115

getOwnPropertyNames列挙できないプロパティを取得できるので、それを使用して、プロトタイプチェーンを上に移動することと組み合わせることができます。

function getAllProperties(obj){
    var allProps = []
      , curr = obj
    do{
        var props = Object.getOwnPropertyNames(curr)
        props.forEach(function(prop){
            if (allProps.indexOf(prop) === -1)
                allProps.push(prop)
        })
    }while(curr = Object.getPrototypeOf(curr))
    return allProps
}

私はそれをSafari 5.1でテストして得ました

> getAllProperties([1,2,3])
["0", "1", "2", "length", "constructor", "push", "slice", "indexOf", "sort", "splice", "concat", "pop", "unshift", "shift", "join", "toString", "forEach", "reduceRight", "toLocaleString", "some", "map", "lastIndexOf", "reduce", "filter", "reverse", "every", "hasOwnProperty", "isPrototypeOf", "valueOf", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "propertyIsEnumerable", "__lookupSetter__"]

更新:コードを少しリファクタリングしました(スペース、中括弧を追加し、関数名を改善しました):

function getAllPropertyNames( obj ) {
    var props = [];

    do {
        Object.getOwnPropertyNames( obj ).forEach(function ( prop ) {
            if ( props.indexOf( prop ) === -1 ) {
                props.push( prop );
            }
        });
    } while ( obj = Object.getPrototypeOf( obj ) );

    return props;
}

1
おかげで、私が理解できないことの1つは次の行です:while(curr = Object.getPrototypeOf(cure))条件ステートメントは比較演算子の代わりに代入演算子を使用するため、これは常にtrueを返しませんか?または、この行は「curr」にプロトタイプがあるかどうかを本質的にチェックしていますか?
dkugappi

2
@AlexNabokovは、結果が偽の場合にfalseをObject.getPrototypeOf(cure)返しますnull。これは、プロトタイプチェーンの先頭に戻るときに発生します。これは、円形のプロトタイプチェーンがないことを前提としています。
Domenic

2
@Alex Function.prototypeは、プロトタイプリンクがを指すため、「ルート」プロトタイプになることはできませんObject.prototype。関数Object.getPrototypeOf( obj )は、のプロトタイプチェーンの最上位のオブジェクトを返しますobj。これにより、プロトタイプチェーンのobj最後(null値)に到達するまで従うことができます。私は...これで、あなたの問題が何であるかわからない
サイムVIDAS

2
@アレックスいいえ、そうではありませんundefined。オブジェクトを(必要に応じてObject.getPrototypeOf(John)返しますBoy.prototype-ここを参照してください:jsfiddle.net/aeGLA/1。コンストラクタBoyはのプロトタイプチェーンにはないことに注意してくださいJohn。のプロトタイプチェーンJohnは次のとおりですBoy.prototype -> Object.prototype -> null
シメビダス

3
Object.getPrototypeOf(obj)がobjのコンストラクタのプロトタイプを返すと思っていました」-はい。の場合John、彼のコンストラクタはBoyであり、のprototypeプロパティはBoyですBoy.prototype。したがって、をObject.getPrototypeOf(John)返しますBoy.prototype
サイムVIDAS

9

再帰を使用したよりクリーンなソリューション:

function getAllPropertyNames (obj) {
    const proto     = Object.getPrototypeOf(obj);
    const inherited = (proto) ? getAllPropertyNames(proto) : [];
    return [...new Set(Object.getOwnPropertyNames(obj).concat(inherited))];
}

編集する

より一般的な関数:

function walkProtoChain (obj, callback) {
    const proto     = Object.getPrototypeOf(obj);
    const inherited = (proto) ? walkProtoChain(proto, callback) : [];
    return [...new Set(callback(obj).concat(inherited))];
}

function getOwnNonEnumPropertyNames (obj) {
    return Object.getOwnPropertyNames(obj)
        .filter(p => !obj.propertyIsEnumerable(p));
}

function getAllPropertyNames (obj) {
    return walkProtoChain(obj, Object.getOwnPropertyNames);
}

function getAllEnumPropertyNames (obj) {
    return walkProtoChain(obj, Object.keys);
}

function getAllNonEnumPropertyNames (obj) {
    return walkProtoChain(obj, getOwnNonEnumPropertyNames);
}

この同じテンプレートはObject.getOwnPropertySymbols、などを使用して適用できます。


4

セットを利用すると、IMOというややクリーンなソリューションになります。

const own = Object.getOwnPropertyNames;
const proto = Object.getPrototypeOf;

function getAllPropertyNames(obj) {
    const props = new Set();
    do own(obj).forEach(p => props.add(p)); while (obj = proto(obj));
    return Array.from(props);
}

2

ES6での単純な反復:

function getAllPropertyNames(obj) {
    let result = new Set();
    while (obj) {
        Object.getOwnPropertyNames(obj).forEach(p => result.add(p));
        obj = Object.getPrototypeOf(obj);
    }
    return [...result];
}

実行例:


1

いくつかのインスタンスのすべての継承されたプロパティまたはメソッドを取得するには、次のようなものを使用できます

var BaseType = function () {
    this.baseAttribute = "base attribute";
    this.baseMethod = function() {
        return "base method";
    };
};

var SomeType = function() {
    BaseType();
    this.someAttribute = "some attribute";
    this.someMethod = function (){
        return "some method";
    };
};

SomeType.prototype = new BaseType();
SomeType.prototype.constructor = SomeType;

var instance = new SomeType();

Object.prototype.getInherited = function(){
    var props = []
    for (var name in this) {  
        if (!this.hasOwnProperty(name) && !(name == 'constructor' || name == 'getInherited')) {  
            props.push(name);
        }  
    }
    return props;
};

alert(instance.getInherited().join(","));

1
使用した方が良いObject.getInheritedのではなくObject.prototype.getInherited。これにより、醜い!(name == 'getInherited')チェックの必要もなくなります。また、実装では、props配列に重複したプロパティを含めることができます。最後に、constructorプロパティを無視する目的は何ですか?
Pauan 2013

object.getInheritedはいつtrueになりますか?私は、継承にこだわっていて、質問の下に確認してください:stackoverflow.com/questions/31718345/...
ラビンドラ・バブー

IMHO-これらはObjectではなくReflectに属します。または-あるいは-言語Object.keys(src、[settings])から期待できます。ここで、オプション設定は、非列挙型を含めるかどうか、継承を含めるかどうか、列挙できないものを含めるかどうか、独自のものを含めるかどうかを指定できます、シンボルを含める場合、そしておそらく最大の継承の深さまで掘り下げます。
Radagast the Brown

ええと... Object.entriesについても同じです。Object.valuesについてはわかりません。...上手。何故なの。
Radagast the Brown

0

これは私が主題を研究している間に思いついた解決策です。objオブジェクトの列挙不可能なすべての所有されていないプロパティをすべて取得するにはgetProperties(obj, "nonown", "nonenum");

function getProperties(obj, type, enumerability) {
/**
 * Return array of object properties
 * @param {String} type - Property type. Can be "own", "nonown" or "both"
 * @param {String} enumerability - Property enumerability. Can be "enum", 
 * "nonenum" or "both"
 * @returns {String|Array} Array of properties
 */
    var props = Object.create(null);  // Dictionary

    var firstIteration = true;

    do {
        var allProps = Object.getOwnPropertyNames(obj);
        var enumProps = Object.keys(obj);
        var nonenumProps = allProps.filter(x => !(new Set(enumProps)).has(x));

        enumProps.forEach(function(prop) {
            if (!(prop in props)) {
                props[prop] = { own: firstIteration, enum_: true };
            }           
        });

        nonenumProps.forEach(function(prop) {
            if (!(prop in props)) {
                props[prop] = { own: firstIteration, enum_: false };
            }           
        });

        firstIteration = false;
    } while (obj = Object.getPrototypeOf(obj));

    for (prop in props) {
        if (type == "own" && props[prop]["own"] == false) {
            delete props[prop];
            continue;
        }
        if (type == "nonown" && props[prop]["own"] == true) {
            delete props[prop];
            continue;
        }

        if (enumerability == "enum" && props[prop]["enum_"] == false) {
            delete props[prop];
            continue;
        }
        if (enumerability == "nonenum" && props[prop]["enum_"] == true) {
            delete props[prop];
        }
    }

    return Object.keys(props);
}

0
function getNonEnumerableNonOwnPropertyNames( obj ) {
    var oCurObjPrototype = Object.getPrototypeOf(obj);
    var arReturn = [];
    var arCurObjPropertyNames = [];
    var arCurNonEnumerable = [];
    while (oCurObjPrototype) {
        arCurObjPropertyNames = Object.getOwnPropertyNames(oCurObjPrototype);
        arCurNonEnumerable = arCurObjPropertyNames.filter(function(item, i, arr){
            return !oCurObjPrototype.propertyIsEnumerable(item);
        })
        Array.prototype.push.apply(arReturn,arCurNonEnumerable);
        oCurObjPrototype = Object.getPrototypeOf(oCurObjPrototype);
    }
    return arReturn;
}

使用例:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

親オブジェクトの列挙不可能なプロパティをログに記録しようとしている場合。デフォルトでは、es6のクラス内で定義されているメソッドはプロトタイプに設定されていますが、列挙不可として設定されています。

Object.getOwnPropertyNames(Object.getPrototypeOf(obj));

0

私の個人的な好みの実装:)

function getAllProperties(In, Out = {}) {
    const keys = Object.getOwnPropertyNames(In);
    keys.forEach(key => Object.defineProperty(In, key, {
        enumerable: true
    }));
    Out = { ...In, ...Out };

    const Prototype = Object.getPrototypeOf(In);
    return Prototype === Object.prototype ? Out : getAllProperties(Proto, Out);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.