È possibile ottenere i nomi delle proprietà ereditate non enumerabili di un oggetto?


99

In JavaScript abbiamo alcuni modi per ottenere le proprietà di un oggetto, a seconda di ciò che vogliamo ottenere.

1) Object.keys(), che restituisce tutte le proprietà enumerabili di un oggetto, un metodo ECMA5.

2) un for...inciclo, che restituisce tutte le proprietà enumerabili di un oggetto, indipendentemente dal fatto che siano proprietà proprie o ereditate dalla catena del prototipo.

3) Object.getOwnPropertyNames(obj)che restituisce tutte le proprietà proprie di un oggetto, enumerabili o meno.

Abbiamo anche metodi che hasOwnProperty(prop)ci consentono di controllare se una proprietà è ereditata o appartiene effettivamente a quell'oggetto e propertyIsEnumerable(prop)che, come suggerisce il nome, ci consente di verificare se una proprietà è enumerabile.

Con tutte queste opzioni, non c'è modo di ottenere una proprietà non enumerabile e non propria di un oggetto, che è ciò che voglio fare. C'è un modo per fare questo? In altre parole, posso in qualche modo ottenere un elenco delle proprietà non enumerabili ereditate?

Grazie.


4
La tua domanda ha risposto alla domanda che stavo per porre: come ispezionare proprietà non enumerabili (solo per esplorare ciò che è disponibile negli oggetti predefiniti). Finalmente ho trovato getOwnPropertyNames! :-)
marcus

1
@marcus :-) Ecco di cosa si tratta SO!
dkugappi

Risposte:


115

Dal momento che getOwnPropertyNamespuoi ottenere proprietà non enumerabili, puoi usarlo e combinarlo con il camminare lungo la catena del prototipo.

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
}

L'ho provato su Safari 5.1 e ho ottenuto

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

Aggiornamento: riformulato un po 'il codice (aggiunti spazi e parentesi graffe e migliorato il nome della funzione):

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
Grazie aby, una cosa che non capisco è la riga: while(curr = Object.getPrototypeOf(cure))poiché l'istruzione condizionale utilizza un operatore di assegnazione invece di un operatore di confronto, non sarebbe sempre vero? O questa linea sta essenzialmente controllando se "curr" ha un prototipo?
dkugappi

2
@AlexNabokov restituirà false se il risultato è falso, cosa che si verificherà quando Object.getPrototypeOf(cure)ritorna nullall'inizio della catena del prototipo. Immagino che questo non presuma catene di prototipi circolari!
Domenic

2
@Alex Function.prototypenon può mai essere il prototipo "root", poiché il suo collegamento al prototipo punta Object.prototype. La funzione Object.getPrototypeOf( obj )restituisce l'oggetto più in alto nella catena di prototipi di obj. Ti consente di seguire la catena del prototipo objfino a raggiungere la sua fine (il nullvalore). Non sono sicuro di quale sia il tuo problema con questo ...
Šime Vidas

2
@Alex No, non lo è undefined. Object.getPrototypeOf(John)restituisce l' Boy.prototypeoggetto (come dovrebbe) - vedi qui: jsfiddle.net/aeGLA/1 . Si noti che il costruttore nonBoy è nella catena dei prototipi di . La catena di prototipi è la seguente: . JohnJohnBoy.prototype -> Object.prototype -> null
Šime Vidas

3
" Pensavo che Object.getPrototypeOf (obj) restituisse il prototipo del costruttore di obj " - Sì. Nel caso di John, il suo costruttore è Boye la prototypeproprietà di Boyè Boy.prototype. Quindi Object.getPrototypeOf(John)ritorna Boy.prototype.
Šime Vidas

9

Una soluzione più pulita utilizzando la ricorsione:

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

modificare

Funzioni più generiche:

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

Questo stesso modello può essere applicato utilizzando Object.getOwnPropertySymbols, ecc.


4

Approfittando dei set porta a una soluzione un po 'più pulita, 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

Semplice iterativo 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];
}

Esempio di esecuzione:


1

Per ottenere tutte le proprietà oi metodi ereditati per qualche istanza potresti usare qualcosa di simile

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
Meglio usare Object.getInheritedpiuttosto che Object.prototype.getInherited. In questo modo si elimina anche la necessità del brutto !(name == 'getInherited')controllo. Inoltre, nella tua implementazione, l' propsarray può contenere proprietà duplicate. Infine, qual è lo scopo di ignorare la constructorproprietà?
Pauan

Quando diventerà vero object.getInherited? Si prega di controllare sotto la domanda poiché sono bloccato con l'ereditarietà: stackoverflow.com/questions/31718345/…
Ravindra babu

IMHO - questi appartengono a Reflect, non a Object. Oppure, in alternativa, mi aspetto dal linguaggio Object.keys (src, [settings]) dove le impostazioni opzionali possono specificare se includere non ninumerables, se includere ereditato, se includere ereditato non enumerabile, se includere il proprio , se includere simboli, e forse a quale profondità massima di ereditarietà scavare.
Radagast the Brown il

uh ... lo stesso per Object.entries. Non sono sicuro di Object.values ​​però. ...bene. perchè no.
Radagast the Brown il

0

Ecco la soluzione che mi è venuta studiando l'argomento. Per ottenere tutte le proprietà non enumerabili objdell'oggetto faregetProperties(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;
}

Esempio di utilizzo:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

se stai cercando di registrare proprietà non enumerabili di un oggetto genitore ex. per impostazione predefinita, i metodi definiti all'interno di una classe in es6 sono impostati su prototype ma sono impostati come non enumerabili.

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

0

Un'implementazione nelle mie preferenze personali :)

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.