(Suppose there is a good reason for this. See the end of the question if you want to read a good reason.)
I would like to get the same result as the for in loop, but without using this language construct. As a result, I mean only an array of property names (I donβt need to reproduce the behavior that will happen if I modify the object during iteration).
To put the question in code, I would like to implement this function without for in :
function getPropertiesOf(obj) { var props = []; for (var prop in obj) props.push(prop); return props; }
From my understanding of the ECMAScript 5.1 specification about the for in statement and the Object.keys method , it seems that the following implementation should be correct:
function getPropertiesOf(obj) { var props = []; var alreadySeen = {}; // Handle primitive types if (obj === null || obj === undefined) return props; obj = Object(obj); // For each object in the prototype chain: while (obj !== null) { // Add own enumerable properties that have not been seen yet var enumProps = Object.keys(obj); for (var i = 0; i < enumProps.length; i++) { var prop = enumProps[i]; if (!alreadySeen[prop]) props.push(prop); } // Add all own properties (including non-enumerable ones) // in the alreadySeen set. var allProps = Object.getOwnPropertyNames(obj); for (var i = 0; i < allProps.length; i++) alreadySeen[allProps[i]] = true; // Continue with the object prototype obj = Object.getPrototypeOf(obj); } return props; }
The idea is to explicitly lay the prototype chain and use Object.keys to get its own properties in each object of the chain. We exclude property names that have already been seen in previous objects in the chain, including when they were considered non-enumerable. This method should even comply with the additional warranty specified in MDN :
The Object.keys() method returns an array of a specific object enumerated properties, in the same order as for...in loop [...].
(my emphasis)
I played a little with this implementation, and I could not break it.
So the question is:
Is my analysis correct? Or do I not notice a specification detail that would make this implementation incorrect?
Do you know another way to do this that would correspond to the specific execution of the for in order in all cases?
Notes:
- I don't care that ECMAScript <5.1.
- I don't care about performance (this can be disastrous).
Edit: to satisfy @lexicore's curiosity (but not really part of the question), a good reason is this. I am developing a compiler for JavaScript (from Scala), and the for in language constructor is not part of what I want to support directly in the intermediate representation of my compiler. Instead, I have a "built-in" getPropertiesOf function, which I mainly show as the first example. I am trying to get rid of as many built-in functions as possible by replacing them with a "user-space" implementation (written in Scala). For performance, I still have an optimizer that sometimes "implements" some methods, in which case it will implement getPropertiesOf with an effective first implementation. But to make an intermediate representation with an audio signal and work when the optimizer is turned off, I need a true implementation of the function, regardless of its cost, if it is fixed. And in this case, I can not use for in , since my IR cannot represent this construct (but I can call arbitrary JavaScript functions for any objects, for example, Object.keys ).