How to Get the Non-Enumerable Inherited Property Names of an Object

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

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

Accessing a object's non enumerable properties

You can write a function, let's call it getAllPropertyNames(), that iterates through the prototype chain of the object and accumulates the properties of each level:

function getAllPropertyNames (o) {  let propertyNames = []
for (let proto = o; proto !== null; proto = Object.getPrototypeOf(proto)) { propertyNames = propertyNames.concat(Object.getOwnPropertyNames(proto)) }
return propertyNames}
console.log(getAllPropertyNames({ mykey: 'value' }))

After Object.create: properties are there, but Object.keys / getOwnPropertyNames = []?

Object.create has two arguments. The first one is the prototype of the created object, and a second optional parameter is an object of property descriptors.

Object.create(proto[, propertiesObject])

If you create the object passing the properties object as the first argument,

let obj = Object.create({x: 1, y: 2});

this one will become the prototype of your new object.
As Object.keys() returns an array of only its own enumerable properties, the ones you passed when constructing it won't be listed.

To create the object the way you intended, yo can use Object.assign:

let obj = Object.assign({}, {x: 1, y: 2});

Why non-enumerable properties don't affect the array built-in methods?

It's because of the way the array iterator is specified. While it's most common to have dense arrays where every key is defined, sparse arrays are also supported, where only a handful of keys are defined. To support this, array iteration needs to be able to iterate past keys that don't actually exist. For example:

const arr = [];arr[0] = 0;arr[10] = 10;
console.log('has 0?', arr.hasOwnProperty(0))console.log('has 1?', arr.hasOwnProperty(1))
for (let val of arr) { console.log(val);}

Is it possible to emulate non-enumerable properties?

You can do it via code-rewriting. Rewrite every use of for (p in o) body to

for (p in o) {
if (!(/^__notenum_/.test(p) || o['__notenum_' + p])) {
body
}
}

and then you can mark properties not enumerable by defining a __notenum_... property. To be compatible you would have to tweak the above to make sure that __notenum_propname is defined at the same prototype level as propname, and if you use them, overwrite eval and new Function to rewrite.

That's basically what ES5/3 does.

Iterate over all properties, not just enumerable ones

  1. When does the loop while (obj) break?

When there are no more prototypes in the prototype chain getPrototypeOf returns null. For regular objects, this happens when you try to get the prototype of Object.prototype.


  1. How do lines within the while block work to add the own property names of obj to result?

Array.prototype.push.apply(result, array) passes each element in the array as argument to result.push. It is like calling result.push(a[0], a[1], ...) where a is Object.getOwnPropertyNames(obj).

Iterating through inherited object properties when hasOwnProperty() is false

for...in only iterates over enumerable properties. All the properties on Object.prototype are not enumerable, so they won't actually be iterated over.

Here is an example that would show inherited properties. We create a new object that inherits from another object, not from Object.prototype:

var foo = {abc: 123};// Creates an object with property `fog: 'stack'` but has `foo` as its prototypevar buz = Object.create(foo, {fog: {value: 'stack', enumerable: true}}); 
for (var name in buz) { if (Object.prototype.hasOwnProperty.call(buz, name)) { console.log('this is fog (' + name + ') for sure. Value: ' + buz[name]); } else { console.log(name); // toString or something else }}


Related Topics



Leave a reply



Submit