Get the same result as the for..in loop, without any loops..in

(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 ).

+6
source share
1 answer

From the point of view of the specification, your analysis will correct it only on the assumption that a particular implementation defines a specific enumeration order for the for-in statement:

If the implementation defines a specific enumeration order for for-in, the same enumeration order should be used in step 5 of this algorithm.

See the last sentence here .

Thus, if the implementation does not provide such a specific order, then in-in and Object.keys can return different things. Well, in this case, even two different inputs can return different things.

Very interesting, the whole story comes down to the question if two input-output will give the same results if the object has not been modified. Because if it’s not, then how could you test β€œanyway”?

In practice, this is likely to be true, but I could also easily imagine that an object can dynamically rebuild its internal structure, between calls "inside". For example, if a particular property is available very often, the implementation may reorganize the hash table so that access to this property is more efficient. As far as I can see, the specification does not prohibit this. And this is also not so unreasonable.

So, the answer to your question: no, there is no guarantee in accordance with the specification, but it will probably work in practice .

Update

I think there is one more problem. Where is it determined, what is the order of the properties between the members of the prototype chain? You can get your own properties in the correct order, but do they merge exactly the way you do it? For example, why are child properties first and parent next?

+3
source

Source: https://habr.com/ru/post/976916/


All Articles