The javascript for keyword will iterate over all properties of an object. If the object is modified within the loop body, what happens?
For example, is the following code OK?
for(var key in obj)
if (whatever(obj[key]))
delete obj[key];
OK would be if this code works in a deterministic fashion and preferably that all keys in obj are tested exactly once. By contrast, in .NET or Java similar constructs will typically throw an exception.
I think it works. Just be careful to ask for hasOwnProperty(key) - because for will also happily iterate over inherited properties (and methods, which are just properties with function values).
Also: http://www.w3schools.com/js/js_loop_for_in.asp says:
Note: The code in the body of the for...in loop is executed once for each property.
Also: https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in says:
A for...in loop iterates over the properties of an object in an arbitrary order (see the delete operator for more on why one cannot depend on the seeming orderliness of iteration, at least in a cross-browser setting). If a property is modified in one iteration and then visited at a later time, the value exposed by the loop will be its value at that later time. A property which is deleted before it has been visited will not then be visited later. Properties added to the object over which iteration is occurring may either be visited or omitted from iteration. In general it is best not to add, modify, or remove properties from the object during iteration, other than the property currently being visited; there is no guarantee whether or not an added property will be visited, whether a modified property will be visited before or after it is modified, or whether a deleted property will be visited before it is deleted.
What I read from this is - if you're modifying values other than the current one, the nondeterminism might bite you in the ass. However, modifying the current one should be okay.
It may be possible to modify an object's properties while iterating over them using for-in. The question is: should you care? It is easy to rewrite the code to first create a list of the properties and then to delete the ones that match the criteria for deletion.
If, for example, you wanted to delete certain properties that are not functions and are not inherited:
var keys = Object.keys(obj).filter(k=>!(obj[k] instanceof Function))
for(var key of keys)
if(whatever(obj[key]))
delete obj[key];
In general, it is better to write code that does not give rise to tricky questions like the one you raise.
Note that this code assumes the existence of Object.keys() in your runtime environment. Older browsers may not support this, so you may need to find a workaround if you need to support those.
Related
First of all, I want to say that I know that extending global methods is not a good practice, but there are situations where a developer needs to avoid duplicating objects due to size or other reasons, and global functions can help a lot in such situations.(at least that's what I believe)
I raised this topic because I think it's strange that this "bug" (example below) still exists in JavaScript, because if I'm not mistaken about 5 years ago I needed to traverse a window object with a loop and I ended up finding it, but since so much time has passed and it still exists I think it might be normal for Object.prototype
Can anyone tell me if it's a forgotten bug or is it a normal occurrence? can't this be exploited as code injection along with this?
Object.prototype.foo = function(){
for(index in this) console.log(index)
}
var A = [1,2,3];
//returns a non-iterable index at the end
A.foo();
function bar(attr){
var B = {a:1, b:2,c:3}
for(index in B) console.log(index);
for(index in attr) console.log(index);
}
//all the 'in' iterations will start printing the index value at the end
bar([1,2,3,4,5]);
for(index in {x:1, y:2,z:3}) console.log(index);
This is completely normal and expected.
for..in iterates over all enumerable own properties on the object in question. Then it does the same for that object's internal prototype, and so on - until it reaches the beginning of the prototype chain (which is often Object.prototype).
If you put an enumerable property onto Object.prototype, then any object (that inherits from Object.prototype) you iterate over with for..in will eventually iterate over that property you added.
can't this be exploited as code injection along with this?
I suppose - malicious code can modify Object.prototype and do a whole lot of other nasty things, so take care not to run anything that isn't trustworthy.
Accidental prototype pollution (even occasionally from user input) isn't an uncommon problem that libraries sometimes run into. A good rule of thumb is to use static key names everywhere - if you need something dynamic, use a Map instead of an object.
The behavior you're observing is normal. The properties defined on Object.prototype will be prototype linked to by its instances. The for..in will iterate over all enumerable properties of an object, whether owned or inherited.
You can access foo at A.__proto__.__proto__.foo.
A is an instance of Array[], which is an instance of the global Object{}.
From MDN Web Docs:
The enumerable property attribute defines whether the property is picked by Object.assign() or spread operator. For non-Symbol properties it also defines whether it shows up in a for...in loop and Object.keys() or not.
You can define the property as an innumerable attribute by setting the enumerable flag to false.
Object.defineProperty(Object.prototype, "foo", {
value: function(){
for(index in this) console.log(index);
},
configurable: true,
enumerable: false
});
You could freeze the the Object.prototype by:
Object.freeze(Object.prototype);
Object.freeze(Object);
to prevent attackers from polluting it.
Is there any way that I can insert an new element into a Map before of or after an existing key?
as for Array would be: arr.splice(<POSITION>, 0, <NEW ELEMENT>);
Thanks!
general theory
A Map does –unlike an array– not have the semantics of “order”.
It's designed to be most efficient for storing&retrieving values for specific keys. (In fact, it's internal implementation does some structuring/ordering specifically optimized for that, “b-tree” being the keyword here.
javascript implementation
Despite all that, mozilla docs say:
A Map object iterates its elements in insertion order — a for...of loop returns an array of [key, value] for each iteration.
update: The official ECMA-262 standard confirms this.
personally, I am guessing, implementors will keep a separate index for order-of-entry and a b-tree for most efficient singular access.
Based on this, what you would have to do is essentially, treating the map like an immutable object, and creating a new one from it:
iterate over the prior map
add each element to a new map. – when the right time comes, insert your element. – Keep iterating for the rest of it
assign your new element to the name of the old one.
I don't now your specific use case, but perhaps your problems could also be solved by sort Iteration to specific criteria right when iterating over it.
I recently asked a question about LocalStorage. Using JSON.parse(localStorage.item) and JSON.parse(localStorage['item']) weren't working to return NULL when the item hadn't been set yet.
However, JSON.parse(localStorage.getItem('item') did work. And it turns out, JSON.parse(localStorage.testObject || null) also works.
One of the comments basically said that localStorage.getItem() and localStorage.setItem() should always be preferred:
The getter and setter provide a consistent, standardised and
crossbrowser compatible way to work with the LS api and should always
be preferred over the other ways. -Christoph
I've come to like using the shorthand dot and bracket notations for localStorage, but I'm curious to know others' take on this. Is localStorage.getItem('item') better than localStorage.item or localStorage['item'] OR as long as they work are the shorthand notations okay?
Both direct property access (localStorage.foo or localStorage['foo']) and using the functional interface (localStorage.getItem('foo')) work fine. Both are standard and cross-browser compatible.* According to the spec:
The supported property names on a Storage object are the keys of each key/value pair currently present in the list associated with the object, in the order that the keys were last added to the storage area.
They just behave differently when no key/value pair is found with the requested name. For example, if key 'foo' does not exist, var a = localStorage.foo; will result in a being undefined, while var a = localStorage.getItem('foo'); will result in a having the value null. As you have discovered, undefined and null are not interchangeable in JavaScript. :)
EDIT: As Christoph points out in his answer, the functional interface is the only way to reliably store and retrieve values under keys equal to the predefined properties of localStorage. (There are six of these: length, key, setItem, getItem, removeItem, and clear.) So, for instance, the following will always work:
localStorage.setItem('length', 2);
console.log(localStorage.getItem('length'));
Note in particular that the first statement will not affect the property localStorage.length (except perhaps incrementing it if there was no key 'length' already in localStorage). In this respect, the spec seems to be internally inconsistent.
However, the following will probably not do what you want:
localStorage.length = 2;
console.log(localStorage.length);
Interestingly, the first is a no-op in Chrome, but is synonymous with the functional call in Firefox. The second will always log the number of keys present in localStorage.
* This is true for browsers that support web storage in the first place. (This includes pretty much all modern desktop and mobile browsers.) For environments that simulate local storage using cookies or other techniques, the behavior depends on the shim that is used. Several polyfills for localStorage can be found here.
The question is already quite old, but since I have been quoted in the question, I think I should say two words about my statement.
The Storage Object is rather special, it's an object, which provides access to a list of key/value pairs. Thus it's not an ordinary object or array.
For example it has the length attribute, which unlike the array length attribute is readonly and returns the number of keys in the storage.
With an array you can do:
var a = [1,2,3,4];
a.length // => 4
a.length = 2;
a // => [1,2]
Here we have the first reason to use the getters/setters. What if you want to set an item called length?
localStorage.length = "foo";
localStorage.length // => 0
localStorage.setItem("length","foo");
// the "length" key is now only accessable via the getter method:
localStorage.length // => 1
localStorage.getItem("length") // => "foo"
With other members of the Storage object it's even more critical, since they are writable and you can accidently overwrite methods like getItem. Using the API methods prevents any of these possible problems and provides a consistent Interface.
Also interesting point is the following paragraph in the spec (emphasized by me):
The setItem() and removeItem() methods must be atomic with respect to failure. In the case of failure, the method does nothing. That is, changes to the data storage area must either be successful, or the data storage area must not be changed at all.
Theoretically there should be no difference between the getters/setters and the [] access, but you never know...
I know it's an old post but since nobody actually mentioned performance I set up some JsPerf tests to benchmark it and as well as being a coherent interface getItem and setItem are also consistently faster than using dot notation or brackets as well as being much easier to read.
Here are my tests on JsPerf
As it was mentioned, there is almost no difference except of nonexisting key. The difference in performance varies depending on what browser/OS are you using. But it is not really that different.
I suggest you to use standard interface, just because it is a recommended way of using it.
Does javascript guarantees that the sequence of keys of an object gets preserved even if the new value is assigned to a key?
For example, if i have the following object
var Object = {
keyX: value1,
keyB: value2,
keyZ: value3
}
If i iterate through keys using for .. in, I get the proper sequence i.e. keyX, keyB, keyZ. and if I change the value of keyB, I am still getting the same sequence in iteration.
My question is, will the sequence remains the same always, or it might change in any case?
Well, it's quite clearly said in the doc (MDN):
A for...in loop iterates over the properties of an object in an
arbitrary order.
And this section of documentation gives more comprehensive explanation to this:
Although ECMAScript makes iteration order of objects
implementation-dependent, it may appear that all major browsers
support an iteration order based on the earliest added property coming
first (at least for properties not on the prototype). However, in the
case of Internet Explorer, when one uses delete on a property, some
confusing behavior results, preventing other browsers from using
simple objects like object literals as ordered associative arrays.
In Explorer, while the property value is indeed set to undefined, if
one later adds back a property with the same name, the property will
be iterated in its old position--not at the end of the iteration
sequence as one might expect after having deleted the property and
then added it back.
I seem to be having an interesting problem only in chrome (not IE, FF). Given the following object:
var myObj = {
"59" : "Hello",
"52" : "and",
"50" : "how",
"31" : "are",
"65" : "you"
};
Going over this object via a for loop spits out the contents in the following order:
for(var j in myObj) { document.write(myObj[j] +', '); }
are, how, and, hello, you
All the other major browser give it in 'proper' order. Chrome is treating the keys as integers instead of strings. The problem is I have a json data source I can't change, and I need to access the items in the order they're in the object.
Can anyone suggest a way to do this in Google Chrome?
When iterating over an object via for...in, the order of properties is not guaranteed. There is nothing you can do:
A for...in loop iterates over the properties of an object in an arbitrary order (see the delete operator for more on why one cannot depend on the seeming orderliness of iteration, at least in a cross-browser setting).
And from the delete page:
Although ECMAScript makes iteration order of objects implementation-dependent, it may appear that all major browsers support an iteration order based on the earliest added property coming first (at least for properties not on the prototype). However, in the case of Internet Explorer, when one uses delete on a property, some confusing behavior results, preventing other browsers from using simple objects like object literals as ordered associative arrays. In Explorer, while the property value is indeed set to undefined, if one later adds back a property with the same name, the property will be iterated in its old position--not at the end of the iteration sequence as one might expect after having deleted the property and then added it back.
So if you want to simulate an ordered associative array in a cross-browser environment, you are forced to either use two separate arrays (one for the keys and the other for the values), or build an array of single-property objects, etc.
If you would like to know why it is like that, feel free to read the very long discussion in the V8 Issue Tracker "Wrong order in Object properties interation":
http://code.google.com/p/v8/issues/detail?id=164