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


99

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

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

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

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

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

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

Спасибо.


4
Ваш вопрос ответил на вопрос, который я собирался задать: как проверять неперечислимые свойства (просто для изучения того, что доступно в предопределенных объектах). Наконец я нашел getOwnPropertyNames! :-)
маркус

1
@marcus :-) Вот что такое ТАК!
dkugappi

Ответы:


115

Поскольку 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;
}

1
Благодаря toby, я не понимаю, что такое строка: while(curr = Object.getPrototypeOf(cure))поскольку в условном выражении используется оператор присваивания вместо оператора сравнения, не всегда ли он возвращает true? Или эта строка по существу проверяет, есть ли у curr прототип?
dkugappi

2
@AlexNabokov он вернет false, если результат ложный, что произойдет при Object.getPrototypeOf(cure)возврате nullв верхней части цепочки прототипов. Я предполагаю, что это не предполагает никаких круговых цепочек прототипов!
Domenic

2
@Alex Function.prototypeникогда не может быть "корневым" прототипом, поскольку ссылка на прототип указывает на Object.prototype. Функция Object.getPrototypeOf( obj )возвращает самый верхний объект в цепочке прототипов obj. Это позволяет вам следовать цепочке прототипов objдо ее конца ( nullзначения). Я не уверен, в чем твоя проблема ...
Шиме Видас

2
@Alex Нет, это не так undefined. Object.getPrototypeOf(John)возвращает Boy.prototypeобъект (как и должно) - см. здесь: jsfiddle.net/aeGLA/1 . Обратите внимание, что конструктор неBoy входит в цепочку прототипов . Прототипом цепочка выглядит следующим образом : . JohnJohnBoy.prototype -> Object.prototype -> null
Шиме Видас

3
« Я думал, что Object.getPrototypeOf (obj) вернет прототип конструктора obj » - Да. В случае John, его конструктор - это Boy, а prototypeсвойство Boy- Boy.prototype. Итак, Object.getPrototypeOf(John)возвращается Boy.prototype.
Шиме Видас

9

Более чистое решение с использованием рекурсии:

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

редактировать

Более общие функции:

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

Этот же шаблон можно применить, используя Object.getOwnPropertySymbolsи т. Д.


4

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

Прямая итерация в ES6:

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

Пример выполнения:


1

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

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
Лучше использовать Object.getInherited, чем Object.prototype.getInherited. Это также устраняет необходимость в уродливой !(name == 'getInherited')проверке. Кроме того, в вашей реализации propsмассив может содержать повторяющиеся свойства. И наконец, какова цель игнорирования constructorсвойства?
Pauan

Когда object.getInherited станет истинным? Пожалуйста, проверьте вопрос ниже, поскольку я застрял с наследованием: stackoverflow.com/questions/31718345/…
Равиндра бабу

IMHO - они принадлежат Reflect, а не Object. Или - в качестве альтернативы - я бы ожидал от языка Object.keys (src, [settings]), где необязательные настройки могут указывать, включать ли нечисловые объекты, если включать наследуемые, если включать неперечислимые наследуемые, если включать собственные , если включать символы, и возможно до какой max глубины наследования копать.
Радагаст Коричневый,

эээ ... то же самое для Object.entries. Однако не уверен в Object.values. ...хорошо. почему нет.
Радагаст Коричневый,

0

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

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

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

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

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

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

0

Реализация в моих личных предпочтениях :)

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);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.