Est-il possible d'obtenir les noms de propriété hérités non énumérables d'un objet?


99

En JavaScript, nous avons plusieurs façons d'obtenir les propriétés d'un objet, en fonction de ce que nous voulons obtenir.

1) Object.keys() , qui renvoie toutes les propriétés propres et énumérables d'un objet, une méthode ECMA5.

2) un for...in boucle, qui renvoie toutes les propriétés énumérables d'un objet, qu'elles soient propres ou héritées de la chaîne de prototypes.

3) Object.getOwnPropertyNames(obj) qui renvoie toutes les propriétés propres d'un objet, énumérables ou non.

Nous avons également des méthodes qui hasOwnProperty(prop)nous permettent de vérifier si une propriété est héritée ou appartient réellement à cet objet, et propertyIsEnumerable(prop)qui, comme son nom l'indique, nous permet de vérifier si une propriété est énumérable.

Avec toutes ces options, il n'y a aucun moyen d'obtenir un propriété non énumérable et non propre d'un objet, ce que je veux faire. Y a-t-il un moyen de faire ça? En d'autres termes, puis-je obtenir d'une manière ou d'une autre une liste des propriétés non énumérables héritées?

Je vous remercie.


4
Votre question répond à la question que j'allais poser: Comment inspecter les propriétés non énumérables (juste pour explorer ce qui est disponible dans les objets prédéfinis). Enfin, j'ai trouvé getOwnPropertyNames! :-)
marcus

1
@marcus :-) C'est ce qu'est SO!
dkugappi

Réponses:


115

Puisque vous getOwnPropertyNamespouvez obtenir des propriétés non énumérables, vous pouvez les utiliser et les combiner avec la progression de la chaîne de prototypes.

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
}

Je l'ai testé sur Safari 5.1 et j'ai obtenu

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

Mise à jour: refactorisation un peu du code (ajout d'espaces et d'accolades et amélioration du nom de la fonction):

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
Grâce à par, une chose que je ne comprends pas est la ligne:, while(curr = Object.getPrototypeOf(cure))comme l'instruction conditionnelle utilise un opérateur d'affectation au lieu d'un opérateur de comparaison, cela ne retournerait-il pas toujours vrai? Ou est-ce que cette ligne vérifie essentiellement si "curr" a un prototype?
dkugappi

2
@AlexNabokov il renverra false si le résultat est faux, ce qui se produira lors du Object.getPrototypeOf(cure)retour nullen haut de la chaîne de prototypes. Je suppose que cela ne suppose aucune chaîne prototype circulaire!
Domenic

2
@Alex Function.prototypene peut jamais être le prototype «racine», puisque son lien prototype pointe vers Object.prototype. La fonction Object.getPrototypeOf( obj )renvoie l'objet le plus haut de la chaîne de prototypes de obj. Il vous permet de suivre la chaîne de prototypes objjusqu'à ce que vous atteigniez sa fin (la nullvaleur). Je ne sais pas quel est votre problème avec ceci ...
Šime Vidas

2
@Alex Non, ce n'est pas le cas undefined. Object.getPrototypeOf(John)renvoie l' Boy.prototypeobjet (comme il se doit) - voir ici: jsfiddle.net/aeGLA/1 . Notez que le constructeur Boyn'est pas dans la chaîne de prototypes de John. La chaîne prototype Johnest la suivante: Boy.prototype -> Object.prototype -> null.
Šime Vidas

3
" Je pensais que Object.getPrototypeOf (obj) renverrait le prototype du constructeur de l'objet " - Oui. Dans le cas de John, son constructeur est Boyet la prototypepropriété de Boyest Boy.prototype. Donc Object.getPrototypeOf(John)revient Boy.prototype.
Šime Vidas

9

Une solution plus propre utilisant la récursivité:

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

Éditer

Fonctions plus génériques:

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

Ce même modèle peut être appliqué en utilisant Object.getOwnPropertySymbols, etc.


4

Profiter des sets conduit à une solution un peu plus propre, l'OMI.

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

Itératif simple dans ES6:

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

Exemple d'exécution:


1

Pour obtenir toutes les propriétés ou méthodes héritées d'une instance, vous pouvez utiliser quelque chose comme ça

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
Mieux vaut utiliser Object.getInheritedplutôt que Object.prototype.getInherited. Faire cela supprime également le besoin de la vilaine !(name == 'getInherited')chèque. De plus, dans votre implémentation, le propstableau peut contenir des propriétés en double. Enfin, quel est le but d'ignorer la constructorpropriété?
Pauan

Quand object.getInherited deviendra-t-il vrai? Veuillez vérifier la question ci-dessous car je suis coincé avec l'héritage: stackoverflow.com/questions/31718345/…
Ravindra babu

IMHO - ceux-ci appartiennent à Reflect, pas à Object. Ou - alternativement - j'attendrais du langage Object.keys (src, [settings]) où les paramètres facultatifs peuvent spécifier s'il faut inclure les non-ninumerables, si pour inclure hérité, si pour inclure non-énumérable hérité, si pour inclure propre , s'il faut inclure des symboles, et peut-être jusqu'à quelle profondeur d'héritage maximale creuser.
Radagast the Brown le

euh ... même chose pour Object.entries. Pas sûr d'Object.values ​​cependant. ...bien. pourquoi pas.
Radagast the Brown le

0

Voici la solution que j'ai trouvée en étudiant le sujet. Pour obtenir toutes les propriétés non propres non énumérables de l' objobjet, faitesgetProperties(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;
}

Exemple d'utilisation:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

si vous essayez de consigner les propriétés non énumérables d'un objet parent ex. par défaut, les méthodes définies dans une classe dans es6 sont définies sur prototype mais sont définies comme non énumérables.

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

0

Une implémentation dans mes préférences personnelles :)

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);
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.