Возможно ли получить неперечислимые наследуемые имена свойств объекта?

В JavaScript у нас есть несколько способов получить свойства объекта, в зависимости от того, что мы хотим получить.

1) Object.keys(), который возвращает все собственные, перечисляемые свойства объекта, метод ECMA5.

2) a for...in loop, который возвращает все перечислимые свойства объекта, независимо от того, являются ли они собственными свойствами или унаследован от цепочки прототипов.

3) Object.getOwnPropertyNames(obj), который возвращает все собственные свойства объекта, перечислимые или нет.

У нас также есть такие методы, как hasOwnProperty(prop) позволяет нам проверить, унаследовано ли свойство или действительно принадлежит этому объекту, и propertyIsEnumerable(prop), который, как следует из названия, позволяет нам проверить, является ли свойство перечислимым.

При всех этих параметрах невозможно получить свойство неперечислимого, не собственного объекта, что и я хочу сделать. Есть какой-либо способ сделать это? Другими словами, могу ли я каким-то образом получить список унаследованных неперечислимых свойств?

Спасибо.

Ответ 1

Так как getOwnPropertyNames может получить вам неперечислимые свойства, вы можете использовать это и объединить его с хождением по цепочке прототипов.

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
}

Я тестировал это на Safari 5.1 и получил

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

Обновление: Обновлен код (добавлены пробелы и фигурные скобки и улучшено имя функции):

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

И просто получить все.. (enum/nonenum, self/inherited.. Пожалуйста, подтвердите.

function getAllPropertyNames( obj ) {
    var props = [];

    do {
        props= props.concat(Object.getOwnPropertyNames( obj ));
    } while ( obj = Object.getPrototypeOf( obj ) );

    return props;
}

Ответ 2

Чтобы получить все унаследованные свойства или методы для некоторого экземпляра, вы можете использовать что-то вроде этого

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(","));

Ответ 3

Вот решение, которое я придумал при изучении предмета. Чтобы получить все неперечислимые свойства, не принадлежащие объекту obj, выполните getProperties(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);
}

Ответ 4

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

Пример использования:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

Ответ 5

если вы пытаетесь зарегистрировать неперечислимые свойства родительского объекта ex. по умолчанию методы, определенные внутри класса в es6, устанавливаются на прототипе, но устанавливаются как неперечислимые.

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

Ответ 6

Использование наборов приводит к несколько более чистому решению, 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);
}