¿Es posible obtener los nombres de propiedad heredados no enumerables de un objeto?

99

En JavaScript tenemos algunas formas de obtener las propiedades de un objeto, dependiendo de lo que queramos obtener.

1) Object.keys(), que devuelve todas las propiedades enumerables propias de un objeto, un método ECMA5.

2) un for...inbucle, que devuelve todas las propiedades enumerables de un objeto, independientemente de si son propiedades propias o heredadas de la cadena del prototipo.

3) Object.getOwnPropertyNames(obj)que devuelve todas las propiedades propias de un objeto, enumerables o no.

También tenemos métodos que hasOwnProperty(prop)nos permiten verificar si una propiedad es heredada o realmente pertenece a ese objeto, y propertyIsEnumerable(prop)que, como sugiere el nombre, nos permite verificar si una propiedad es enumerable.

Con todas estas opciones, no hay forma de obtener una propiedad no enumerable ni propia de un objeto, que es lo que quiero hacer. ¿Hay alguna forma de hacer esto? En otras palabras, ¿puedo de alguna manera obtener una lista de las propiedades heredadas no enumerables?

Gracias.

dkugappi
fuente
4
Su pregunta respondió a la pregunta que iba a hacer: Cómo inspeccionar propiedades no enumerables (solo para explorar lo que está disponible en objetos predefinidos). ¡Finalmente encontré getOwnPropertyNames! :-)
marcus
1
@marcus :-) ¡De eso se trata SO!
dkugappi

Respuestas:

115

Dado que getOwnPropertyNamespuede obtener propiedades no enumerables, puede usar eso y combinarlo con subir la cadena 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
}

Probé eso en Safari 5.1 y obtuve

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

Actualización: se refactorizó un poco el código (se agregaron espacios y llaves, y se mejoró el nombre de la función):

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;
}
aeropuertoyh
fuente
1
Gracias a Dios, una cosa que no entiendo es la línea:, while(curr = Object.getPrototypeOf(cure))ya que la declaración condicional usa un operador de asignación en lugar de un operador de comparación, ¿esto no siempre devolvería verdadero? ¿O esta línea esencialmente verifica si "curr" tiene un prototipo?
dkugappi
2
@AlexNabokov devolverá falso si el resultado es falso, lo que ocurrirá cuando Object.getPrototypeOf(cure)regrese nullen la parte superior de la cadena de prototipos. ¡Supongo que esto supone que no hay cadenas de prototipos circulares!
Domenic
2
@Alex Function.prototypenunca puede ser el prototipo "raíz", ya que su enlace de prototipo apunta a Object.prototype. La función Object.getPrototypeOf( obj )devuelve el objeto superior en la cadena de prototipos de obj. Le permite seguir la cadena de prototipos de objhasta llegar a su final (el nullvalor). No estoy seguro de cuál es su problema con esto ...
Šime Vidas
2
@ Alex No, no lo es undefined. Object.getPrototypeOf(John)devuelve el Boy.prototypeobjeto (como debería); consulte aquí: jsfiddle.net/aeGLA/1 . Tenga en cuenta que el constructor noBoy está en la cadena de prototipos de . La cadena de prototipo es el siguiente: . JohnJohnBoy.prototype -> Object.prototype -> null
Šime Vidas
3
" Pensé que Object.getPrototypeOf (obj) devolvería el prototipo del constructor de obj " - Sí. En el caso de John, su constructor es Boy, y la prototypepropiedad de Boyes Boy.prototype. Así que Object.getPrototypeOf(John)vuelve Boy.prototype.
Šime Vidas
9

Una solución más limpia usando recursividad:

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

Editar

Funciones más genéricas:

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

Esta misma plantilla se puede aplicar usando Object.getOwnPropertySymbols, etc.

Josh Klodnicki
fuente
4

Aprovechar los conjuntos conduce a una solución algo más limpia, en mi opinión.

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);
}
rico remer
fuente
2

Iterativo directo en ES6:

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

Ejecución de ejemplo:

no polaridad
fuente
1

Para obtener todas las propiedades o métodos heredados para alguna instancia, puede usar algo como esto

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(","));
Milán Jaric
fuente
1
Es mejor usar en Object.getInheritedlugar de Object.prototype.getInherited. Hacer eso también elimina la necesidad del !(name == 'getInherited')cheque feo . Además, en su implementación, la propsmatriz puede contener propiedades duplicadas. Por último, ¿cuál es el propósito de ignorar la constructorpropiedad?
Pauan
¿Cuándo se hará realidad object.getInherited? Por favor, consulte la siguiente pregunta, ya que estoy atascado con la herencia: stackoverflow.com/questions/31718345/…
Ravindra babu
En mi humilde opinión, estos pertenecen a Reflect, no a Object. O, alternativamente, esperaría del lenguaje Object.keys (src, [settings]) donde la configuración opcional puede especificar si incluir no ninumerables, si incluir heredados, si incluir heredados no enumerables, si incluir propios , si incluir símbolos, y quizás hasta qué profundidad de herencia máxima excavar.
Radagast the Brown
uh ... lo mismo para las entradas de Objeto. Sin embargo, no estoy seguro acerca de Object.values. ...bien. Por qué no.
Radagast the Brown
0

Aquí está la solución que se me ocurrió mientras estudiaba el tema. Para obtener todas las propiedades no propias no enumerables del objobjeto, hagagetProperties(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);
}
golem
fuente
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;
}

Ejemplo de uso:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);
Dmitry Ragozin
fuente
0

si está intentando registrar propiedades no enumerables de un objeto principal, ej. de forma predeterminada, los métodos definidos dentro de una clase en es6 se establecen en el prototipo, pero se establecen como no enumerables.

Object.getOwnPropertyNames(Object.getPrototypeOf(obj));
Rahil Ahmad
fuente
0

Una implementación en mis preferencias personales :)

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