Is it possible to get the non-enumerable inherited property names of an object?
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.
Solution 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;
}
Solution 2:
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.