Javascript for-in loop bizarre behavior - javascript

Suddenly, I am seeing a strange behavior.
Here is a snippet:
// attributesList is an object with properties 0, 1, 2.
for (var j in attributesList) {
// this loop should run 3 times.
var attribute = attributesList[j];
}
As you can see from above, the loop should run 3 times. But for some very strange reason it is being run 4 times. For the last iteration the value of j == "seek".
Even stranger, this same code base works on another git branch (no changes). This only seems to happen when I run this from a particular git branch.
Is there any logical explanation for this mysterious "seek" property?
One thing I tried looking into is perhaps javascript version and any other versioning differences... but no luck here.
* Updated *
attributesList is an array of object type.

The object in question has four enumerable properties. It probably inherits seek from its prototype object, which is why you think it only has three properties.
Your comment to MikeC adds weight to that:
when I evaluate the attributesList during run/debug time I do not see any property called "seek"
As does your update:
attributesList is an array of object type.
That tells us that somewhere in your codebase (probably in a plugin), someone has been very naughty and added an enumerable property to Array.prototype. They can do that with various different syntaxes; if it's just the one property (seek), it's probably like this:
Array.prototype.seek = function() {
// ...
};
That's poor practice, they should have made it non-enumerable:
Object.defineProperty(Array.prototype, "seek", {
value: function() {
// ...
}
});
But fundamentally, for-in is not for looping through arrays. See my other answer here for how to loop through arrays.
Two excerpts from that long answer:
Use forEach:
attributesList.forEach(function(attribute) {
// ...
});
or if you really want to use for-in, add a hasOwnProperty check:
for (var j in attributesList) {
if (attributesList.hasOwnProperty(j)) {
// this loop should run 3 times.
var attribute = attributesList[j];
}
}
or of course use a for loop. See the answer for details.

Related

Skip over a for loop if object is empty

I come mainly from a python background so I would expect for...in looping over an empty object {} to skip to loop all together. However this is not the case.
I have a loop that simplifies down to this:
for (let y in x) {
console.log(x[y]);
if (x[y]['amount'] >= 0){
x[y]['amount'] -= 1;
}
}
when the class method is run with x = {} it logs undefined indicating the loop has run.
Coming from python I would expect it to skip over the loop like this similar piece of code:
for y in x:
print(y)
if x[y]['amount'] >= 0:
x[y]['amount'] -= 1
Where it does not print anything because the loop does not run.
Is there anyway to achieve a similar result in JavaScript where the loop would not run if the object is empty?
Thank you.
For an empty object, that loop shouldn't run: https://repl.it/repls/SharpEducatedColdfusion. You may be just seeing the output undefined from the console as Hamms mentioned in the comments. Or there is a separate, unrelated issue / console.log giving you that output.
Update: I was curious, so I did some more digging. It turns out tslint (a linting tool for Typescript) actually requires a check in order to use the for...in loop for this exact reason. This answer does a great job of explaining the problem (which is the same problem had in this question) and providing strong solutions for it.
I'll summarize here. As I mentioned before, the essence of the problem is based on how the for...in loop looks for any properties on the prototype chain:
The loop will iterate over all enumerable properties of the object itself and those the object inherits from its constructor's prototype (properties closer to the object in the prototype chain override prototypes' properties). (MDN).
The solution that best fits your use case is:
for (let y in x) {
if (x.hasOwnProperty(y)) {
...
}
}
This ensures that y is actually defined on the object x, not just on it's prototype chain. For anyone who landed on this question that wants to loop over all the fields defined on the object, this solution is for you:
for (const field of Object.keys(x)) {
...
}
Something related which could cause confusion like this is that your x object or something it inherits from has a y property on it's prototype but that is not defined or has a falsy value on x. You can prevent that with:
if (x.y) {
... your logic / loop here ...
}
Note that the above check will match any falsy value.
If x.y is false or null or something else falsy, you can be more explicit with your check.
if (x.y !== undefined) {
... your logic / loop here ...
}
To learn more about how the compiler and let declaration keyword operate, I really like Kyle Simpson's book "You Don't Know JS - Scope and Closures".

Javascript persistent-sorting Object "class"

Attending to it's specification, JSON elements (and javascript objects) are unordered so, even in almost all cases, when you iterate over a javascript object, you get elements in the same order they was defined; you definitively cannot trust in that order because engine is allowed to alter it.
This is extremely rare. I have been able to observe it one time, but I don't find that code right now and I don't remember the exact version of JS engine (but it was node). If I manage to find it, I will add it to this post.
That being said, the point is that code relying in this behaviour can (and should) be considered buggy because, even it will work as expected in most engines, it may fail typically because of internal engine optimisations.
For example:
"use strict";
var x = {
b: 23,
a: 7
};
function someSorting(input) {
var output = {};
Object.keys(input).sort().map(
function(k){
output[k] = input[k];
}
);
return output;
};
x = someSorting(x);
// Some smart engine could notice that input and output objects have the
// exact same properties and guess that, attending the unordered nature of
// javascript Object, there is no need to actually execute someSorting();
console.log(x);
// Usually will display: { a: 7, b: 23 }
// But perfectly we could got: { b: 23, a: 7 }
I know there is too many literature (even StackOverflow questions) about this (NON-) issue and "workarrounds" to achieve the expected behaviour by sorting keys in a separate array.
But doing so code goes too messy compared in simply trusting in key order.
I'm pretty sure that this can be achieved in a more elegant fashion by implementing a so-called "sObject" alternative having native Object as its prototype but overloading it's native iterator and setter so that:
When any new property is added, it's key is appended to an Array index mantained under the hood.
When an sObject instance is iterated, our customized iterator uses that index to retrieve elements in the right order.
In summary: Actual Object specification is right because, in most cases, properties order doesn't care. So I think that engine optimisations that could mess it are wonderfull.
But it would be also wonderful to have an alternative sObject with which we could do something like:
var x = new sObject({b: 23, a: 7});
...and trust that we could iterate it in the same exact order or, also / at least, do some sorting task over it and trust that this will not be altered.
Of course!! I'm initalyzing it with a native javascript Object so, in fact, theoretically we can't trust that it will be populated right (even I can't imagine why any engine optimisation should alter it before any operation).
I used that notation for brevity (and, I confess) because I expect that, in that case should work always (even I'm not really sure). However we even could sort it later (which, in most cases we will do that way) or use other kind of initialization like providing a JSON string or an array of objects (or arrays) with single key and value pairs.
My concern is: Such a thing exists yet? I wasn't able to find it. But sure I'm not the first guy thinking in that...
I can try to implement it (I'm thinking about that). I think it's possible and that I could achieve it. But it's not as simple so first I want to be sure that I'm not reinventing the wheel...
So any comments, suggestions, etc... will be welcome.
Sure, you could do all this. You will need some machinery such as Object.observer, which is currently only available in Chrome. We can define this as the following:
function myObject(object) {
// if the object already has keys, bring them in in whatever order.
var keys = Object.keys(object);
// Override Object.keys to return our list of keys.
Object.defineProperty(object, 'keys', { get: function() { return keys; });
// Watch the object for new or deleted properties.
// Add new ones at the end, to preserve order.
Object.observe(object, function(changes) {
changes.forEach(function(change) {
if (change.type === 'add') keys.push(change.name);
if (change.type === 'delete') keys = keys.filter(function(key) {
return key === change.name;
});
});
});
return object;
}
Note that Object.observe is asynchronous, so even after you add a property to the object, it won't be reflected in the custom keys property until after a tick of the clock, although in theory you could use Object.deliverChangedRecords.
The above approach uses a function which adds the new ordered key functionality to an existing object. Of course there are other ways to design this.
This "solution" obviously cannot control the behavior of for...in loops.

Obtain the same result as a for..in loop, without any for..in loop

(Let us suppose that there is a good reason for wishing this. See the end of the question if you want to read the good reason.)
I would like to obtain the same result as a for in loop, but without using that language construct. By result I mean only an array of the property names (I don't need to reproduce the behavior that would happen if I modify the object while iterating over it).
To put the question into code, I'd 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 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's prototype
obj = Object.getPrototypeOf(obj);
}
return props;
}
The idea is to walk explicitly the prototype chain, and use Object.keys to get the own properties in each object of the chain. We exclude property names already seen in previous objects in the chain, including when they were seen as non-enumerable. This method should even respect the additional guarantee mentioned on MDN:
The Object.keys() method returns an array of a given object's own
enumerable properties, in the same order as that provided by a
for...in loop [...].
(emphasis is mine)
I played a bit with this implementation, and I haven't been able to break it.
So the question:
Is my analysis correct? Or am I overlooking a detail of the spec that would make this implementation incorrect?
Do you know another way to do this, that would match the implementation's specific order of for in in all cases?
Remarks:
I don't care about ECMAScript < 5.1.
I don't care about performance (it can be disastrous).
Edit: to satisfy #lexicore's curiosity (but not really part of the question), the good reason is the following. I develop a compiler to JavaScript (from Scala), and the for in language construct is not part of the things I want to support directly in the intermediate representation of my compiler. Instead, I have a "built-in" function getPropertiesOf which is basically what I show as first example. I'm trying to get rid of as many builtins as possible by replacing them by "user-space" implementations (written in Scala). For performance, I still have an optimizer that sometimes "intrinsifies" some methods, and in this case it would intrinsify getPropertiesOf with the efficient first implementation. But to make the intermediate representation sound, and work when the optimizer is disabled, I need a true implementation of the feature, no matter the performance cost, as long as it's correct. And in this case I cannot use for in, since my IR cannot represent that construct (but I can call arbitrary JavaScript functions on any objects, e.g., Object.keys).
From the specification point of view, your analysis correct only under assumption that a particular implementation defines a specific order of enumeration for the for-in statement:
If an implementation defines a specific order of enumeration for the
for-in statement, that same enumeration order must be used in step 5
of this algorithm.
See the last sentence here.
So if an implementation does not provide such specific order, then for-in and Object.keys may return different things. Well, in this case even two different for-ins may return different things.
Quite interesting, the whole story reduces to the question if two for-ins will give the same results if the object was not changed. Because, if it is not the case, then how could you test "the same" anyway?
In practice, this will most probably be true, but I could also easily imagine that an object could rebuild its internal structure dynamically, between for-in calls. For instance, if certain property is accessed very often, the implementation may restructure the hash table so that access to that property is more efficient. As far as I can see, the specification does not prohibit that. And it is also not-so-unreasonable.
So the answer to your question is: no, there is no guarantee according to the specification, but still will probably work in practice.
Update
I think there's another problem. Where is it defined, what the order of properties between the members of the prototype chain is? You may get the "own" properties in the right order, but are they merged exactly the way as you do it? For instance, why child properties first and parent's next?

Will this for-in loop detection snippet generate unwanted false positives?

We all know that for-in-loops on arrays are absolutely evil. Still, they are often used and the caused errors are complicated to trace down, especially when happening browser-dependent for example because of indexOf-shims or such.
So, I have coded this simple snippet which adds an enumerable getter for a "error" property on Array.prototype (not for use in production code):
Object.defineProperty(Array.prototype, "error", {
enumerable: true,
get: function() {
if (this === Array.prototype) // that looks OK
return undefined;
if (window.confirm("Somebody who coded the site you're viewing runs through an Array with a for-in-loop.\nShame on him!\n\nDo you want to raise an Error to trace the origin?"))
throw new SyntaxError("Array traverse with for-in-loop, touching Array.prototype's 'error' property :-)");
}
});
You can add it as a greasemonkey script for all domains, and you will see alerts on nearly every site :-) Most of them are caused by calls to jQuery.extend with questionable arguments, btw.
My question is now: Are there any situations that legitimate such "wrong" loops, or anything else causing false-positive alerts?
I am wondering how this would affect the usefulness of my code.
Yes. Legitimacy can often be subjective, but...
As an example, perhaps I have a sparse array, where I have only set values at the indexes with data:
var a = [];
a[123123] = "foo";
a[1233123] = "bar";
If I wanted to iterate over the elements that I defined in this array, then I would use the for...in construct. Even if I coded it defensively, your script would still trigger (a false positive)...
for (var prop in a) {
if (a.hasOwnProperty(prop)) {
// this is a legitimate array element
}
}
See also Why is using "for...in" with array iteration a bad idea? for more information and opinions.

Rhino does not enumerate 'arguments' to a function

I'm trying to use the 'arguments' variable available to a function in order to enumerate the arguments passed to a function, in Javascript, using :
for (var i in arguments){
...
}
This seems to be working for me in the chrome and firebug consoles, while does not work with Rhino. With the former two, I can successfully enter the for loop and see the arguments, while with the latter, it doesn't seem like the for loop is even entered.
Why is this happening and how can I prevent this ?
From Javascript for..in looping over arguments ie.for( arg in arguments) does not work in IE8 but it works in Chrome 8 :
First of all, while the arguments object available within a function is not an array, it is "array-like" enough such that an incremental for loop (for (var i = 0, len = arguments.length; i < len; i++) { ... }) is preferable - not only because it runs faster, but also because it avoids other pitfalls - one of which is exactly what you're falling into.
To actually answer the question of why the second loop doesn't work, it's important to realize just what for ... in loop does: it iterates through all enumerable properties found in an object. Now, I've bolded 2 words in that statement, because I used these two words purposefully to indicate a couple of nuances that, while they may seem subtle, can drastically affect the behavior of your code if you don't realize what's going on.
First let's focus on all - by which I mean to say, not just properties of the object itself, but also potentially properties said object has inherited from its prototype, or its prototype's prototype, or so on. For this reason, it is very often recommended that you "guard" any for ... in loop by immediately additionally qualifying it with the condition if (obj.hasOwnProperty(p)) (assuming your loop were written for (var p in obj)).
But that's not what you're running into here. For that, let's focus on that second word, enumerable. All properties of objects in JavaScript are either enumerable or non-enumerable, which pretty much directly relates to whether the property shows up in a for ... in loop or not. In browsers such as Firefox and IE, as it turns out, the arguments object's numeric properties are not enumerable (nor is its length as it were), which is precisely why you're not iterating through anything!

Categories

Resources