Ist es möglich, die nicht aufzählbaren geerbten Eigenschaftsnamen eines Objekts abzurufen?


98

In JavaScript gibt es verschiedene Möglichkeiten, die Eigenschaften eines Objekts abzurufen, je nachdem, was wir abrufen möchten.

1) Object.keys(), die alle eigenen, aufzählbaren Eigenschaften eines Objekts zurückgibt, eine ECMA5-Methode.

2) eine for...inSchleife, die alle aufzählbaren Eigenschaften eines Objekts zurückgibt, unabhängig davon, ob es sich um eigene Eigenschaften handelt oder von der Prototypenkette geerbt wurde.

3) Object.getOwnPropertyNames(obj)die alle eigenen Eigenschaften eines Objekts zurückgibt, ob aufzählbar oder nicht.

Wir haben auch Methoden, mit denen hasOwnProperty(prop)wir überprüfen können, ob eine Eigenschaft geerbt wurde oder tatsächlich zu diesem Objekt gehört, und mit propertyIsEnumerable(prop)denen wir, wie der Name schon sagt, überprüfen können, ob eine Eigenschaft aufzählbar ist.

Mit all diesen Optionen gibt es keine Möglichkeit, eine nicht aufzählbare, nicht eigene Eigenschaft eines Objekts zu erhalten, was ich tun möchte. Gibt es eine Möglichkeit, dies zu tun? Mit anderen Worten, kann ich irgendwie eine Liste der geerbten nicht aufzählbaren Eigenschaften erhalten?

Danke dir.


4
Ihre Frage beantwortete die Frage, die ich stellen wollte: So überprüfen Sie nicht aufzählbare Eigenschaften (nur um zu untersuchen, was in vordefinierten Objekten verfügbar ist). Endlich habe ich getOwnPropertyNames gefunden! :-)
Marcus

1
@marcus :-) Darum geht es bei SO!
Dkugappi

Antworten:


116

Da getOwnPropertyNamesSie nicht aufzählbare Eigenschaften erhalten, können Sie diese verwenden und mit dem Auflaufen der Prototypenkette kombinieren.

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
}

Ich habe das auf Safari 5.1 getestet und bekommen

> 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__"]

Update: Der Code wurde ein wenig überarbeitet (Leerzeichen und geschweifte Klammern hinzugefügt und der Funktionsname verbessert):

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
Dank byby verstehe ich die Zeile nicht: Da while(curr = Object.getPrototypeOf(cure))die bedingte Anweisung einen Zuweisungsoperator anstelle eines Vergleichsoperators verwendet, würde dies nicht immer true zurückgeben? Oder prüft diese Zeile im Wesentlichen, ob "curr" einen Prototyp hat?
dkugappi

2
@AlexNabokov es wird return false , wenn das Ergebnis falsy ist, die auftritt, wenn Object.getPrototypeOf(cure)kehrt nullan der Spitze der Prototypkette. Ich denke, dies setzt keine kreisförmigen Prototypenketten voraus!
Domenic

2
@Alex Function.prototypekann niemals der "Root" -Prototyp sein, da der Prototyp-Link darauf verweist Object.prototype. Die Funktion Object.getPrototypeOf( obj )gibt das oberste Objekt in der Prototypenkette von zurück obj. Sie können der Prototypenkette folgen, objbis Sie das Ende (den nullWert) erreicht haben. Ich bin nicht sicher, was Ihr Problem damit ist ...
Šime Vidas

2
@ Alex Nein, ist es nicht undefined. Object.getPrototypeOf(John)gibt das Boy.prototypeObjekt zurück (wie es sollte) - siehe hier: jsfiddle.net/aeGLA/1 . Beachten Sie, dass der Konstruktor Boyist nicht in der Prototypkette von John. Die Prototypkette von Johnist wie folgt : Boy.prototype -> Object.prototype -> null.
Šime Vidas

3
" Ich dachte, Object.getPrototypeOf (obj) gibt den Prototyp des Konstruktors des Objekts zurück " - Ja. Im Fall von Johnist sein Konstruktor Boyund das prototypeEigentum von Boyist Boy.prototype. Also Object.getPrototypeOf(John)kehrt zurück Boy.prototype.
Šime Vidas

8

Eine sauberere Lösung mit Rekursion:

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

Bearbeiten

Allgemeinere Funktionen:

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);
}

Dieselbe Vorlage kann mit Object.getOwnPropertySymbolsusw. angewendet werden .


4

Die Nutzung von Sets führt zu einer etwas saubereren Lösung, 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

Einfach iterativ in ES6:

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

Beispiellauf:


1

Um alle geerbten Eigenschaften oder Methoden für eine bestimmte Instanz abzurufen, können Sie so etwas verwenden

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
Besser zu verwenden Object.getInheritedals Object.prototype.getInherited. Dadurch entfällt auch die Notwendigkeit einer hässlichen !(name == 'getInherited')Überprüfung. In Ihrer Implementierung kann das propsArray auch doppelte Eigenschaften enthalten. Was ist der Zweck, die constructorEigenschaft zu ignorieren ?
Pauan

Wann wird object.getInherited wahr? Bitte überprüfen Sie die folgende Frage, da ich mit der Vererbung festgefahren
Ravindra babu

IMHO - diese gehören zu Reflect, nicht zu Object. Oder - alternativ - ich würde von der Sprache Object.keys (src, [settings]) erwarten, dass optionale Einstellungen angeben können, ob nicht-neunumerable, geerbte, nicht-enumerable geerbte, eigene enthalten sollen , wenn Symbole eingeschlossen werden sollen und möglicherweise bis zu welcher maximalen Vererbungstiefe gegraben werden soll.
Radagast the Brown

äh ... dasselbe gilt für Object.entries. Ich bin mir jedoch nicht sicher über Object.values. ...Gut. warum nicht.
Radagast the Brown

0

Hier ist die Lösung, die ich während des Studiums gefunden habe. Um alle nicht aufzählbaren nicht eigenen Eigenschaften des objObjekts zu erhalten, tun Sie diesgetProperties(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;
}

Anwendungsbeispiel:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

Wenn Sie versuchen, nicht aufzählbare Eigenschaften eines übergeordneten Objekts zu protokollieren, z. Standardmäßig werden die in einer Klasse in es6 definierten Methoden für den Prototyp festgelegt, jedoch als nicht aufzählbar.

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

0

Eine Umsetzung in meine persönlichen Vorlieben :)

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);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.