Is it possible to get the non-enumerable inherited property names of an object?

后端 未结 9 1747
温柔的废话
温柔的废话 2020-11-22 12:46

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, enu

相关标签:
9条回答
  • 2020-11-22 13:29

    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);
    }
    
    0 讨论(0)
  • 2020-11-22 13:34
    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);
    
    0 讨论(0)
  • 2020-11-22 13:35

    An implementation in my personal preferences :)

    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);
    }
    
    0 讨论(0)
  • 2020-11-22 13:36

    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(","));
    
    0 讨论(0)
  • 2020-11-22 13:40

    A cleaner solution using recursion:

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

    Edit

    More generic functions:

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

    This same template can be applied using Object.getOwnPropertySymbols, etc.

    0 讨论(0)
  • 2020-11-22 13:41

    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;
    }
    
    0 讨论(0)
提交回复
热议问题