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

JavascriptOopObjectProperties

Javascript Problem Overview


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.

Javascript Solutions


Solution 1 - Javascript

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

Solution 2 - Javascript

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.

Solution 3 - Javascript

Straight forward iterative in ES6:

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

Example run:

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

let obj = {
  abc: 123,
  xyz: 1.234,
  foobar: "hello"
};

console.log(getAllPropertyNames(obj));

Solution 4 - Javascript

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

Solution 5 - Javascript

Update Jan 2022 -- almost every answer combined, plus symbols (ES6+)

After seeing Mozilla's JS documentation specifically say: "no single mechanism iterates all of an object's properties; the various mechanisms each include different subsets of properties."... I had exactly this question albeit newer because I want symbol keys also and I think all the answers above pre-date those).

I came here hoping someone else knew how to create such a single mechanism.

No single answer on this page seems to cover EVERYTHING, but between all of them, I think it can be done -- including the option to also exclude the annoying top level keys.

In exploring the code in Mozilla's JS doc'n that airportyh's answer inspired, plus the table below it on that same page I discovered the new Reflect.ownKeys. That catches everything (including symbols)... except inherited properties, but airportyh's answer walking the prototype chain fixes that.

So... combining all those findings and simplifying as much as I could, I came up with the following two functions, that (I believe) DO catch EVERYTHING. Posting in case it helps anyone else.

Option 1. Simple: Return EVERY Key, no exceptions

Returns every key, enumerable or not, string, symbol, own, inherited, and top level.

function getAllKeys(obj) {
	let keys = [];
    // if primitive (primitives still have keys) skip the first iteration
    if (!(obj instanceof Object)) {
        obj = Object.getPrototypeOf(obj)
    }
	while (obj) {
		keys = keys.concat(Reflect.ownKeys(obj));
		obj = Object.getPrototypeOf(obj);
	}
	return keys;
}

I really like the simplicity, though I wonder if I've missed anything. If any one catches any errors in that, please do let me know.

Option 2. Flexible: Return EVERY Key, with optional exclusions

Adds:

  1. a filter function based on a bunch of one line functions (easier to debug that way, and this ain't code golf ) that determine if any given key should be excluded, based on the parameters passed in,
  2. a condition to walk the prototype chain or not (per airportyh's answer), and,
  3. a condition to stop or not before the top level is reached (per Maciej Krawczyk's answer).

Include or exclude:

  • enumerable keys
  • non-enumerable keys
  • symbol keys
  • string keys
  • own keys
  • inherited keys
  • top level keys.

(On a side note, I'm no JS expert, so maybe I'm missing something. I'm a little confused why no one else here has used Array.prototype.filter(), since isn't that exactly what we're doing?)

I believe the following covers it. By default everything is included except top level keys. Adjust to taste. Again I'd welcome feedback if any errors here:

function getAllKeysConditionally(obj, includeSelf = true, includePrototypeChain = true, includeTop = false, includeEnumerables = true, includeNonenumerables = true, includeStrings = true, includeSymbols = true) {
	
	// Boolean (mini-)functions to determine any given key's eligibility:
	const isEnumerable = (obj, key) => Object.propertyIsEnumerable.call(obj, key);
	const isString = (key) => typeof key === 'string';
	const isSymbol = (key) => typeof key === 'symbol';
	const includeBasedOnEnumerability = (obj, key) => (includeEnumerables && isEnumerable(obj, key)) || (includeNonenumerables && !isEnumerable(obj, key));
	const includeBasedOnKeyType = (key) => (includeStrings && isString(key)) || (includeSymbols && isSymbol(key));
	const include = (obj, key) => includeBasedOnEnumerability(obj, key) && includeBasedOnKeyType(key);
	const notYetRetrieved = (keys, key) => !keys.includes(key);
	
	// filter function putting all the above together:
	const filterFn = key => notYetRetrieved(keys, key) && include(obj, key);
	
	// conditional chooses one of two functions to determine whether to exclude the top level or not:
	const stopFn = includeTop ? (obj => obj === null) : (obj => Object.getPrototypeOf(obj) === null);
	
	// and now the loop to collect and filter everything:
	let keys = [];
	while (!stopFn(obj, includeTop)) {
		if (includeSelf) {
			const ownKeys = Reflect.ownKeys(obj).filter(filterFn);
			keys = keys.concat(ownKeys);
		}
		if (!includePrototypeChain) { break; }
		else {
			includeSelf = true;
			obj = Object.getPrototypeOf(obj);
		}
	}
	return keys;
}

As noted by Jeff Hykin in the comments these solutions use Reflect and arrow functions which are new in ES6. Therefore ES6 minimum required.

Solution 6 - Javascript

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

Solution 7 - Javascript

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

Solution 8 - Javascript

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

Solution 9 - Javascript

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

Solution 10 - Javascript

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

Solution 11 - Javascript

You usually don't want to include Object prototype properties such as __defineGetter__, hasOwnProperty, __proto__ and so on.

This implementation allows you to either include or exclude Object prototype properties:

function getAllPropertyNames(object, includeObjectPrototype = false) {
  const props = Object.getOwnPropertyNames(object);

  let proto = Object.getPrototypeOf(object);
  const objectProto = Object.getPrototypeOf({});

  while (proto && (includeObjectPrototype || proto !== objectProto)) {
    for (const prop of Object.getOwnPropertyNames(proto)) {
      if (props.indexOf(prop) === -1) {
        props.push(prop);
      }
    }
    proto = Object.getPrototypeOf(proto);
  }

  return props;
}

console.log(getAllPropertyNames(new Error('Test'), true));
// ["fileName", "lineNumber", "columnNumber", "message", "toString", "name", "stack", "constructor", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__"]
console.log(getAllPropertyNames(new Error('Test'), false));
// [ "fileName", "lineNumber", "columnNumber", "message", "toString", "name", "stack", "constructor" ]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondkugappiView Question on Stackoverflow
Solution 1 - JavascriptairportyhView Answer on Stackoverflow
Solution 2 - JavascriptJosh KlodnickiView Answer on Stackoverflow
Solution 3 - JavascriptnonopolarityView Answer on Stackoverflow
Solution 4 - Javascriptrich remerView Answer on Stackoverflow
Solution 5 - JavascriptDavidTView Answer on Stackoverflow
Solution 6 - JavascriptRahil AhmadView Answer on Stackoverflow
Solution 7 - JavascriptMilan JaricView Answer on Stackoverflow
Solution 8 - JavascriptgolemView Answer on Stackoverflow
Solution 9 - JavascriptDmitry RagozinView Answer on Stackoverflow
Solution 10 - Javascriptuser3389370View Answer on Stackoverflow
Solution 11 - JavascriptMaciej KrawczykView Answer on Stackoverflow