É 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.