É possível obter os nomes de propriedade herdados não enumeráveis ​​de um objeto?


99

Em JavaScript, temos algumas maneiras de obter as propriedades de um objeto, dependendo do que desejamos obter.

1) Object.keys(), que retorna todas as propriedades enumeráveis ​​próprias de um objeto, um método ECMA5.

2) um for...inloop, que retorna todas as propriedades enumeráveis ​​de um objeto, independentemente de serem propriedades próprias ou herdadas da cadeia de protótipos.

3) Object.getOwnPropertyNames(obj)que retorna todas as propriedades próprias de um objeto, enumeráveis ​​ou não.

Também temos métodos que hasOwnProperty(prop)nos permitem verificar se uma propriedade é herdada ou realmente pertence a esse objeto e propertyIsEnumerable(prop)que, como o nome sugere, nos permitem verificar se uma propriedade é enumerável.

Com todas essas opções, não há como obter uma propriedade não enumerável e não própria de um objeto, que é o que desejo fazer. Há alguma maneira de fazer isso? Em outras palavras, posso de alguma forma obter uma lista das propriedades não enumeráveis ​​herdadas?

Obrigado.


4
Sua pergunta respondeu à pergunta que eu ia fazer: como inspecionar propriedades não enumeráveis ​​(apenas para explorar o que está disponível em objetos predefinidos). Finalmente encontrei getOwnPropertyNames! :-)
— marcus

1
@marcus :-) Isso é o que significa SO!
— dkugappi

Respostas:


115

Como getOwnPropertyNamespode obter propriedades não enumeráveis, você pode usar isso e combiná-lo com a caminhada até a cadeia de protótipos.

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
}

Eu testei isso no Safari 5.1 e consegui

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

Atualização: Refatorou o código um pouco (adicionou espaços e chaves e melhorou o nome da função):

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
Graças a toby, uma coisa que não entendo é a linha:, while(curr = Object.getPrototypeOf(cure))como a instrução condicional usa um operador de atribuição em vez de um operador de comparação, isso não retornaria sempre verdadeiro? Ou esta linha está essencialmente verificando se "curr" tem um protótipo?
— dkugappi

2
@AlexNabokov retornará falso se o resultado for falso, o que ocorrerá quando Object.getPrototypeOf(cure)retornar nullao topo da cadeia de protótipos. Acho que isso não pressupõe cadeias circulares de protótipo!
— Domenic

2
@Alex Function.prototypenunca pode ser o protótipo "raiz", pois seu link de protótipo aponta para Object.prototype. A função Object.getPrototypeOf( obj )retorna o objeto mais alto na cadeia de protótipos de obj. Ele permite que você siga a cadeia de protótipos de objaté chegar ao seu fim (o nullvalor). Não tenho certeza de qual é o seu problema com isso ...
— Šime Vidas

2
@Alex Não, não é undefined. Object.getPrototypeOf(John)retorna o Boy.prototypeobjeto (como deveria) - veja aqui: jsfiddle.net/aeGLA/1 . Observe que o construtor nãoBoy está na cadeia de protótipos de . A corrente do protótipo é a seguinte: . JohnJohnBoy.prototype -> Object.prototype -> null
— Šime Vidas

3
" Achei que Object.getPrototypeOf (obj) retornará o protótipo do construtor do obj " - Sim. No caso de John, seu construtor é Boy, e a prototypepropriedade de Boyé Boy.prototype. Então Object.getPrototypeOf(John)retorna Boy.prototype.
— Šime Vidas

9

Uma solução mais limpa usando recursão:

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

Editar

Funções mais 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);
}

Este mesmo modelo pode ser aplicado usando Object.getOwnPropertySymbols, etc.


4

Tirar proveito dos Sets leva a uma solução um tanto mais limpa, 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

Iterativo direto no ES6:

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

Exemplo de execução:


1

Para obter todas as propriedades ou métodos herdados para alguma instância, você pode usar algo assim

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
Melhor usar Object.getInheriteddo que Object.prototype.getInherited. Isso também elimina a necessidade do !(name == 'getInherited')cheque feio . Além disso, em sua implementação, a propsmatriz pode conter propriedades duplicadas. Por último, qual é o propósito de ignorar a constructorpropriedade?
— Pauan

Quando object.getInherited se tornará true? Verifique a pergunta abaixo, pois estou preso à herança: stackoverflow.com/questions/31718345/…
— Ravindra babu

IMHO - pertencem a Reflect, não a Object. Ou - alternativamente - eu esperaria da linguagem Object.keys (src, [settings]) onde as configurações opcionais podem especificar se incluir não-ninumeráveis, se incluir herdados, se incluir não enumeráveis ​​herdados, se incluir próprios , se incluir símbolos, e talvez até que profundidade de herança máxima cavar.
— Radagast the Brown,

uh ... o mesmo para Object.entries. Não tenho certeza sobre Object.values ​​embora. ...bem. Por que não.
— Radagast the Brown,

0

Aqui está a solução que eu encontrei enquanto estudava o assunto. Para obter todas as propriedades não enumeráveis ​​não próprias do objobjeto, façagetProperties(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;
}

Exemplo de uso:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

se você está tentando registrar propriedades não enumeráveis ​​de um objeto pai ex. por padrão, os métodos definidos dentro de uma classe em es6 são configurados no prototype, mas são configurados como não enumeráveis.

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

0

Uma implementação em minhas preferências pessoais :)

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);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.