Is it possible to get the non-enumerable inherited

2019-01-01 02:21发布

问题:

In JavaScript we have a few ways of getting the properties of an object, depending on what we want to get.

1) Object.keys(), which returns all own, enumerable properties of an object, an ECMA5 method.

2) a for...in loop, which returns all the enumerable properties of an object, regardless of whether they are own properties, or inherited from the prototype chain.

3) Object.getOwnPropertyNames(obj) which returns all own properties of an object, enumerable or not.

We also have such methods as hasOwnProperty(prop) lets us check if a property is inherited or actually belongs to that object, and propertyIsEnumerable(prop) which, as the name suggests, lets us check if a property is enumerable.

With all these options, there is no way to get a non-enumerable, non-own property of an object, which is what I want to do. Is there any way to do this? In other words, can I somehow get a list of the inherited non-enumerable properties?

Thank you.

回答1:

Since getOwnPropertyNames can get you non-enumerable properties, you can use that and combine it with walking up the prototype chain.

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
}

I tested that on Safari 5.1 and got

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

Update: Refactored the code a bit (added spaces, and curly braces, and improved the function name):

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

And to simply get everything..(enum/nonenum, self/inherited.. Please confirm..

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

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

    return props;
}


回答2:

To get all inherited properties or methods for some instance you could use something like this

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:

Here is the solution that I came up with while studying the subject. To get all non-enumerable non-own properties of the obj object do 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;
}

Example of using:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);


回答5:

if you are trying to log non enumerable properties of a parent object ex. by default the methods defined inside a class in es6 are set on prototype but are set as non-enumerable.

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


回答6:

Taking advantage of Sets leads to a somewhat cleaner solution, 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);
}