In lodash you can find the following functions:
_.forOwn
_.forOwnRight
Example for forOwn function iterates properties in a-z order, but says:
iteration order is not guaranteed
While forOwnRight claims to be iterating in opposite (z-a) order.
This does not make much sense to me, so there are 2 questions
Why does forOwnRight even exist if there is no guarantee on iteration order?
Why can't the order be guaranteed?
The order is not guaranteed but is consistent. It means that _.forOwnRight is guaranteed to provide the result reversed from _.forOwn
It's as per specification: it does not state how object's properties must be ordered so it's up to JS engine how to handle it (and they do that differently for performance reasons).
A note: the order depends not only on the particular ES implementation, but also on runtime, since modern JS VMs do a lot of heuristics in runtime.
For the ones curious about optimizations here is a good link (it seriously does not fit to cover in this Q/A):
https://developers.google.com/v8/design
Looking at the source we can see that:
function baseForOwn(object, iteratee) {
return baseFor(object, iteratee, keys);
}
function baseForOwnRight(object, iteratee) {
return baseForRight(object, iteratee, keys);
}
function baseFor(object, iteratee, keysFunc) {
var index = -1,
iterable = toObject(object),
props = keysFunc(object),
length = props.length;
while (++index < length) {
var key = props[index];
if (iteratee(iterable[key], key, iterable) === false) {
break;
}
}
return object;
}
function baseForRight(object, iteratee, keysFunc) {
var iterable = toObject(object),
props = keysFunc(object),
length = props.length;
while (length--) {
var key = props[length];
if (iteratee(iterable[key], key, iterable) === false) {
break;
}
}
return object;
}
Both of the functions internally rely on keysFunc, which returns the keys of the passed object. Since the order of an object's keys is not strictly defined the order cannot be known beforehand, however both of the methods internally use the same method, so the reversed order is guaranteed to be an exact reversal.
I think it is easier to answer question 2 first.
Both functions forOwn and forOwnRight works with objects, therefore, the order of the properties is not guaranteed, as stated in 4.3.3 Objects of the Ecma-262 specification :
[An object] is an unordered collection of properties each of which
contains a primitive value, object, or function.
Usually the properties are printed by VMs in their insertion order, but that should not be taken as a general fact. Refer also to this question for more details.
Though, assuming forOwn processes the properties in the following order [a, c, b] on a particular javascript runtime, you have a guarantee that forOwnRight will process the properties in the order [b, c, a]. Therefore there is sense to have both methods.
Related
Why are objects not iterable by default?
I see questions all the time related to iterating objects, the common solution being to iterate over an object's properties and accessing the values within an object that way. This seems so common that it makes me wonder why objects themselves aren't iterable.
Statements like the ES6 for...of would be nice to use for objects by default. Because these features are only available for special "iterable objects" which don't include {} objects, we have to go through hoops to make this work for objects we want to use it for.
The for...of statement creates a loop Iterating over iterable objects
(including Array, Map, Set, arguments object and so on)...
For example using an ES6 generator function:
var example = {a: {e: 'one', f: 'two'}, b: {g: 'three'}, c: {h: 'four', i: 'five'}};
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
for (let [key, value] of entries(example)) {
console.log(key);
console.log(value);
for (let [key, value] of entries(value)) {
console.log(key);
console.log(value);
}
}
The above properly logs data in the order I expect it to when I run the code in Firefox (which supports ES6):
By default, {} objects are not iterable, but why? Would the disadvantages outweigh the potential benefits of objects being iterable? What are the issues associated with this?
In addition, because {} objects are different from "Array-like" collections and "iterable objects" such as NodeList, HtmlCollection, and arguments, they can't be converted into Arrays.
For example:
var argumentsArray = Array.prototype.slice.call(arguments);
or be used with Array methods:
Array.prototype.forEach.call(nodeList, function (element) {}).
Besides the questions I have above, I would love to see a working example on how to make {} objects into iterables, especially from those who have mentioned the [Symbol.iterator]. This should allow these new {} "iterable objects" to use statements like for...of. Also, I wonder if making objects iterable allow them to be converted into Arrays.
I tried the below code, but I get a TypeError: can't convert undefined to object.
var example = {a: {e: 'one', f: 'two'}, b: {g: 'three'}, c: {h: 'four', i: 'five'}};
// I want to be able to use "for...of" for the "example" object.
// I also want to be able to convert the "example" object into an Array.
example[Symbol.iterator] = function* (obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
};
for (let [key, value] of example) { console.log(value); } // error
console.log([...example]); // error
I'll give this a try. Note that I'm not affiliated with ECMA and have no visibility into their decision-making process, so I cannot definitively say why they have or have not done anything. However, I'll state my assumptions and take my best shot.
1. Why add a for...of construct in the first place?
JavaScript already includes a for...in construct that can be used to iterate the properties of an object. However, it's not really a forEach loop, as it enumerates all of the properties on an object and tends to only work predictably in simple cases.
It breaks down in more complex cases (including with arrays, where its use tends to be either discouraged or thoroughly obfuscated by the safeguards needed to for use for...in with an array correctly). You can work around that by using hasOwnProperty (among other things), but that's a bit clunky and inelegant.
So therefore my assumption is that the for...of construct is being added to address the deficiencies associated with the for...in construct, and provide greater utility and flexibility when iterating things. People tend to treat for...in as a forEach loop that can be generally applied to any collection and produce sane results in any possible context, but that's not what happens. The for...of loop fixes that.
I also assume that it's important for existing ES5 code to run under ES6 and produce the same result as it did under ES5, so breaking changes cannot be made, for instance, to the behavior of the for...in construct.
2. How does for...of work?
The reference documentation is useful for this part. Specifically, an object is considered iterable if it defines the Symbol.iterator property.
The property-definition should be a function that returns the items in the collection, one, by, one, and sets a flag indicating whether or not there are more items to fetch. Predefined implementations are provided for some object-types, and it's relatively clear that using for...of simply delegates to the iterator function.
This approach is useful, as it makes it very straightforward to provide your own iterators. I might say the approach could have presented practical issues due to its reliance upon defining a property where previously there was none, except from what I can tell that's not the case as the new property is essentially ignored unless you deliberately go looking for it (i.e. it will not present in for...in loops as a key, etc.). So that's not the case.
Practical non-issues aside, it may have been considered conceptually controversial to start all objects off with a new pre-defined property, or to implicitly say that "every object is a collection".
3. Why are objects not iterable using for...of by default?
My guess is that this is a combination of:
Making all objects iterable by default may have been considered unacceptable because it adds a property where previously there was none, or because an object isn't (necessarily) a collection. As Felix notes, "what does it mean to iterate over a function or a regular expression object"?
Simple objects can already be iterated using for...in, and it's not clear what a built-in iterator implementation could have done differently/better than the existing for...in behavior. So even if #1 is wrong and adding the property was acceptable, it may not have been seen as useful.
Users who want to make their objects iterable can easily do so, by defining the Symbol.iterator property.
The ES6 spec also provides a Map type, which is iterable by default and has some other small advantages over using a plain object as a Map.
There's even an example provided for #3 in the reference documentation:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
for (var value of myIterable) {
console.log(value);
}
Given that objects can easily be made iterable, that they can already be iterated using for...in, and that there's likely not clear agreement on what a default object iterator should do (if what it does is meant to be somehow different from what for...in does), it seems reasonable enough that objects were not made iterable by default.
Note that your example code can be rewritten using for...in:
for (let levelOneKey in object) {
console.log(levelOneKey); // "example"
console.log(object[levelOneKey]); // {"random":"nest","another":"thing"}
var levelTwoObj = object[levelOneKey];
for (let levelTwoKey in levelTwoObj ) {
console.log(levelTwoKey); // "random"
console.log(levelTwoObj[levelTwoKey]); // "nest"
}
}
...or you can also make your object iterable in the way you want by doing something like the following (or you can make all objects iterable by assigning to Object.prototype[Symbol.iterator] instead):
obj = {
a: '1',
b: { something: 'else' },
c: 4,
d: { nested: { nestedAgain: true }}
};
obj[Symbol.iterator] = function() {
var keys = [];
var ref = this;
for (var key in this) {
//note: can do hasOwnProperty() here, etc.
keys.push(key);
}
return {
next: function() {
if (this._keys && this._obj && this._index < this._keys.length) {
var key = this._keys[this._index];
this._index++;
return { key: key, value: this._obj[key], done: false };
} else {
return { done: true };
}
},
_index: 0,
_keys: keys,
_obj: ref
};
};
You can play with that here (in Chrome, at lease): http://jsfiddle.net/rncr3ppz/5/
Edit
And in response to your updated question, yes, it is possible to convert an iterable to an array, using the spread operator in ES6.
However, this doesn't seem to be working in Chrome yet, or at least I cannot get it to work in my jsFiddle. In theory it should be as simple as:
var array = [...myIterable];
Objects don't implement the iteration protocols in Javascript for very good reasons. There are two levels at which object properties can be iterated over in JavaScript:
the program level
the data level
Program Level Iteration
When you iterate over an object at the program level you examine a portion of the structure of your program. It is a reflective operation. Let's illustrate this statement with an array type, which is usually iterated over at the data level:
const xs = [1,2,3];
xs.f = function f() {};
for (let i in xs) console.log(xs[i]); // logs `f` as well
We just examined the program level of xs. Since arrays store data sequences, we are regularly interested in the data level only. for..in evidently makes no sense in connection with arrays and other "data-oriented" structures in most cases. That is the reason why ES2015 has introduced for..of and the iterable protocol.
Data Level Iteration
Does that mean that we can simply distinguish the data from the program level by distinguishing functions from primitive types? No, because functions can also be data in Javascript:
Array.prototype.sort for instance expects a function to perform a certain sort algorithm
Thunks like () => 1 + 2 are just functional wrappers for lazily evaluated values
Besides primitive values can represent the program level as well:
[].length for instance is a Number but represents the length of an array and thus belongs to the program domain
That means that we can't distinguish the program and data level by merely checking types.
It is important to understand that the implementation of the iteration protocols for plain old Javascript objects would rely on the data level. But as we've just seen, a reliable distinction between data and program level iteration is not possible.
With Arrays this distinction is trivial: Every element with an integer-like key is a data element. Objects have an equivalent feature: The enumerable descriptor. But is it really advisable to rely on this? I believe it is not! The meaning of the enumerable descriptor is too blurry.
Conclusion
There is no meaningful way to implement the iteration protocols for objects, because not every object is a collection.
If object properties were iterable by default, program and data level were mixed-up. Since every composite type in Javascript is based on plain objects this would apply for Array and Map as well.
for..in, Object.keys, Reflect.ownKeys etc. can be used for both reflection and data iteration, a clear distinction is regularly not possible. If you're not careful, you end up quickly with meta programming and weird dependencies. The Map abstract data type effectively ends the conflating of program and data level. I believe Map is the most significant achievement in ES2015, even if Promises are much more exciting.
I was also bothered with this question.
Then I came up with an idea of using Object.entries({...}), it returns an Array which is an Iterable.
Also, Dr. Axel Rauschmayer posted an excellent answer on this.
See Why plain objects are NOT iterable
I guess the question should be "why is there no built-in object iteration?
Adding iterability to objects themselves could conceivably have unintended consequences, and no, there is no way to guarantee order, but writing an iterator is as simple as
function* iterate_object(o) {
var keys = Object.keys(o);
for (var i=0; i<keys.length; i++) {
yield [keys[i], o[keys[i]]];
}
}
Then
for (var [key, val] of iterate_object({a: 1, b: 2})) {
console.log(key, val);
}
a 1
b 2
You can easily make all objects iterable globally:
Object.defineProperty(Object.prototype, Symbol.iterator, {
enumerable: false,
value: function * (){
for(let key in this){
if(this.hasOwnProperty(key)){
yield [key, this[key]];
}
}
}
});
This is the latest approach (which works in chrome canary)
var files = {
'/root': {type: 'directory'},
'/root/example.txt': {type: 'file'}
};
for (let [key, {type}] of Object.entries(files)) {
console.log(type);
}
Yes entries is now a method thats part of Object :)
edit
After looking more into it, it seems you could do the following
Object.prototype[Symbol.iterator] = function * () {
for (const [key, value] of Object.entries(this)) {
yield {key, value}; // or [key, value]
}
};
so you can now do this
for (const {key, value:{type}} of files) {
console.log(key, type);
}
edit2
Back to your original example, if you wanted to use the above prototype method it would like like this
for (const {key, value:item1} of example) {
console.log(key);
console.log(item1);
for (const {key, value:item2} of item1) {
console.log(key);
console.log(item2);
}
}
Technically, this is not an answer to the question why? but I have adapted Jack Slocum’s answer above in light of BT’s comments to something which can be used to make an Object iterable.
var iterableProperties={
enumerable: false,
value: function * () {
for(let key in this) if(this.hasOwnProperty(key)) yield this[key];
}
};
var fruit={
'a': 'apple',
'b': 'banana',
'c': 'cherry'
};
Object.defineProperty(fruit,Symbol.iterator,iterableProperties);
for(let v of fruit) console.log(v);
Not quite as convenient as it should have been, but it’s workable, especially if you have multiple objects:
var instruments={
'a': 'accordion',
'b': 'banjo',
'c': 'cor anglais'
};
Object.defineProperty(instruments,Symbol.iterator,iterableProperties);
for(let v of instruments) console.log(v);
And, because every one is entitled to an opinion, I can’t see why Objects are not already iterable either. If you can polyfill them as above, or use for … in then I can’t see a simple argument.
One possible suggestion is that what is iterable is a type of object, so it is possible that iterable has been limited to a subset of objects just in case some other objects explode in the attempt.
I noticed that when enumerating the properties of an object, that it seems like a snapshot of the current properties is taken at start of the loop, and then the snapshot is iterated. I feel this way because the following doesn't create an endless loop:
var obj = {a:0,b:0}, i=0;
for (var k in obj) {
obj[i++] = 0;
}
alert(i) // 2
demo http://jsfiddle.net/kqzLG/
The above code demonstrates that I'm adding new properties, but the new properties won't get enumerated.
However, the delete operator seems to defy my snapshot theory. Here's the same code, but deleting a property before it gets enumerated.
var obj = {a:0,b:0}, i=0;
for (var k in obj) {
i++;
delete obj.b;
}
alert(i) // 1
demo http://jsfiddle.net/Gs2vh/
The above code demonstrates that the loop body only executed one time. It would have executed twice if the snapshot theory were true.
What's going on here? Does javascript have some type of hidden iterator that it uses, and the delete operator is somehow aware of it?
-- I realize that I'm assuming something about iteration order- specifically that iteration occurs based off of property insertion time. I believe all browsers use such an implementation.
Interesting question. The answer lies in the specification (emphasis mine):
The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified. Properties of the object being enumerated may be deleted during enumeration. If a property that has not yet been visited during enumeration is deleted, then it will not be visited. If new properties are added to the object being enumerated during enumeration, the newly added properties are not guaranteed to be visited in the active enumeration. A property name must not be visited more than once in any enumeration.
So it is explicitly specified that a deleted property must not be traversed anymore. However, the behaviour for adding a new property is implementation dependent, most likely because it is not defined how properties should be stored internally.
For example in Chrome, it seems that numeric properties are stored before alphabetical ones:
> Object.keys({a:0, 0:1});
["0", "a"]
However, even if you add alphabetical keys:
var obj = {a:0,b:0};
for (var k in obj) {
obj['c'] = 0;
console.log(k);
}
c does not seem to be traversed, the output is a b.
Firefox shows the same behaviour, although keys are stored in insertion order:
> Object.keys({a:0, 0:1});
["a", "0"]
obj[i++] = 0;
First iteration:
Entry: i = 0, Exit, i = 1
Second iteration:
Entry: i = 1, Exit, i = 2
Moreover, javascript for in loops are not guaranteed to be done in order when iterating on an object. Thus, delete obj.b; produces unpredictable results.
This is more of a general practices question.
The language I am working with is Javascript. I have a function that is getting an object with many variables (okay, just 10 variables). What is the best way to make sure this function is getting all the required variables and that they are all set?
I know, I know, why not just use a if statement. That's a big chunk of if statements! As I grow as a programmer, I know that may not be the best method for this. I'm looking for a shortcut actually. How would you check a large sum of variables for existence and non-blank values?
This is a pretty neat way of handling validation, I usually use this when checking for required fields in form inputs.
var theObj = { /* object loaded from server */ }
function checkTheObj(testObj)
{
var requiredKeys = ['key1', 'key2', 'key3'];
for(var keyPos = 0; keyPos < requiredKeys.length; keyPos++)
{
if(typeof(testObj[requiredKeys[keyPos]]) == 'undefined')
{
return false;
}
}
return true;
}
if(checkTheObj(theObj))
{
//do stuff
}
You can of course tweak this to return or throw an exception telling the first missing field (or use an internal array to return a list of all missing fields).
function objectHas(obj, properties) {
var len = properties.length
for (var i=0; i<len; i++) {
if (i in properties) {
if((!obj.hasOwnProperty(properties[i])) || (!obj.propertyIsEnumerable(properties[i]))) {
return false;
}
}
}
return true;
}
Usage:
if(objectHas(user, ["email", "password", "phone"])) {
console.log("awesome");
}
It's simple, but does the job.
Edit: On an ideal world you could extend the Object prototype for an even neater syntax such as if(object.has(["a", "b", "c"])), but apparently extending the Object prototype is the incarnation of evil so a function will have to do :)
First of all, you need to improve your understanding of these languages and learn the correct terminology.
There is no (single) language named "Javascript" at all. You are implicitly using several languages here (depending on the runtime environment), all of which are ECMAScript implementations, and one of which is Netscape/Mozilla JavaScript (in Mozilla-based software like Firefox).
An object does not have variables, it has properties (not: keys). Global code, function code, and eval code can have variables; that is a different (but similar) concept.
The function is not getting an object, it is being passed a reference to an object as argument.
As a programmer, you should already know that you can do repetitive tasks in a loop; the associated statements in ECMAScript implementations are for, for-in, while and do. So you do not have to write several if statements.
You can access the properties of an object in two ways, where property is the property name:
Dot notation: obj.property
Bracket notation: obj["property"]
The second one is equivalent to the first if the property name is an identifier, i.e. if it follows certain naming rules. If the property name is not an identifier or if it is variable, you have to use the second one. This also shows that all property names are string values. So you can store the name of a property as value of a variable or another property, and then access the variable or property in the property accessor. In the following, a property name (property) is stored in and used from a variable:
var propertyName = "property";
obj[propertyName]
Combining that with a loop allows you to iterate over certain properties of an object. Unfortunately, the solutions presented so far are flawed in two respects: A for-in statement iterates only over the enumerable properties of an object, and it does so in arbitrary order. In addition, it also iterates over the enumerable inherited properties (which is why one solution requires the hasOwnProperty() call).
A simple, sure and efficient way to iterate only over certain properties of an object in a defined order looks as follows:
var propertyNames = ['name1', 'name2', 'name3'];
for (var i = 0, len = propertyNames.length; i < len; ++i)
{
/* … */ myObject[propertyNames[i]] /* … */
}
This works because propertyNames refers to an Array instance, which encapsulates an array data structure. The elements of an array are the properties of the Array instance that have integer indexes from 0 to 65535 (232−1). Because indexes are not identifiers (they start with a decimal digit), you have to use the bracket property accessor syntax (some people misunderstand this and refer to all ECMAScript objects as "arrays", even call them "associative arrays" and […] the "Array operator"). Therefore, propertyNames[i] evaluates to the values of the elements of the array in each iteration as i is increased by 1 each time. As a result, myObject[propertyNames[i]] accesses the property with that name in each loop.
Now, to find out whether the property is set, you need to define what that means. Accessing a property that does not exist results in the undefined value (not in an error). However an existing property may also have the undefined value as its value.
If "not set" means that the object does not have the property (but may inherit it), then you should use hasOwnProperty() as used in Mahn's solution.
If "not set" means that the object does not have the property and does not inherit it, then you should use the in operator, provided that the object is not a host object (because the in operator is not specified for them):
if (propertyNames[i] in obj)
If "not set" means that the object either has or inherits the property, but the property has the undefined value, or the object neither has nor inherits the property, then you should use the typeof operator as used in Bob Davies' and aetaur's solutions (but the latter approach using Array.prototype.every() is less compatible as-is; that method was not specified before ECMAScript Edition 5, and is not available in IE/JScript < 9).
There is a third option with ECMAScript Edition 5.x, the Object.keys() method which (despite its name) returns a reference to an Array instance that holds the names of all not-inherited properties of the argument:
var propertyNames = Object.keys(obj);
/* continue as described above */
It is a good idea to emulate Object.keys() if it is not built-in, as this algorithm is frequently useful.
This expression returns true, if all variables from variableNameList (list of required variable names) set in object o:
variableNameList.every(function(varName){ return typeof o[varName] !== 'undefined'; });
You can use underscore _.all function instead of native every, and underscore _.isUndefined instead of typeof ....
I am thinking to use the following function:
function delDups(arr){
var out=[],obj={};
for(var i=0,len=arr.length;i<len;i++){
obj[arr[i]]=0;
}
for(i in obj){
out.push(i);
}
return out;
}
The function is slightly modified, original at be found here
However, I am sure there are some values that will make it crash, and I want to know exactly what values will (so I can do soemthing about it)
Well, if arr isn't an array-like object (e.g. with a length and indexed properties) it will crash.
It may however also not do what you expect whenever the data in arr isn't an array of strings. In that case you'd get an array back with strings only, the original objects and their data type will be lost. But it will not crash...
Crashing is not the only inconvenient outcome from executing code. Also of (perhaps greater) interest are cases where the code runs but returns an incorrect result. Your analysis might be something like:
for(var i=0,len=arr.length;i<len;i++){
In the above, it is assumed that the value of arr.length is a numeric value greater than or equal to zero. If arr does not have a length property, or its value isn't a non-negative number, the for loop will not behave as expected (e.g. it may result in an error or an infinite loop).
obj[arr[i]]=0;
In this line, the result of evaluating arr[i] is used as a property name, so wherever that expression returns something that isn't suitable as a property name, you will get an error, e.g. if arr[i] is an ActiveX object, you can expect unexpected outcomes. If it's a native Object the value will be the result of calling its toString method, which might provide the same value for different objects, or an error, or "just work".
for(i in obj){
will iterate over all enumerable properties of obj, including those it inherits. If an enumerable property is added to Object.prototype, it will turn up in the loop so it's common to use a hasOwnProperty test to filter out inherited properties.
How much you test for errors depends on the environment you expect the code to be used in. If you have reasonable control and have documented the values that are expected to be passed to the function (e.g. array of primitive values) then it is reasonable to do minimal (or no) testing of input values. If someone passes in an ActiveX object instead of an array and it goes belly up, you respond with "RTFM".
On the other hand, if it is known that the code will be used in a library in uncontrolled and widely varying situations, testing that the input has a non-negative, numeric length property seems sensible, as does adding a hasOwnProperty test to the for..in loop.
How much time and effort you put into making your code robust is a function of where you expect it to run, but adding some sensible and obvious checks up front may well save some grief later. So I'd do something like:
function delDups(arr) {
var out=[],obj={};
var len = arr && arr.length;
if (len && len > 0) {
for(var i=0; i<len; i++) {
obj[arr[i]] = 0;
for (i in obj) {
if (obj.hasOwnProperty(i)) {
out.push(i);
}
}
}
return out;
}
Woohoo! I crashed it, where is my prize?
var arr3 = delDups(eval());
I faced a strange behaviour in Javascript. I get
"Object doesn't support this property or method"
exception for the removeAttribute function in the following code:
var buttons = controlDiv.getElementsByTagName("button");
for ( var button in buttons )
button.removeAttribute('disabled');
When I change the code with the following, the problem disappears:
var buttons = controlDiv.getElementsByTagName("button");
for ( var i = 0; i < buttons.length; i++ )
buttons[i].removeAttribute('disabled');
What is the value of button inside the for...in?
Don't use for..in for Array iteration.
It's important to understand that Javascript Array's square bracket syntax ([]) for accessing indicies is actually inherited from the Object...
obj.prop === obj['prop'] // true
The for..in structure does not work like a more traditional for..each/in that would be found in other languages (php, python, etc...).
Javascript's for..in is designed to iterate over the properties of an object. Producing the key of each property. Using this key combined with the Object's bracket syntax, you can easily access the values you are after.
var obj = {
foo: "bar",
fizz: "buzz",
moo: "muck"
};
for ( var prop in obj ) {
console.log(prop); // foo / fizz / moo
console.log(obj[prop]); // bar / buzz / muck
}
And because the Array is simply an Object with sequential numeric property names (indexes) the for..in works in a similar way, producing the numeric indicies just as it produces the property names above.
An important characteristic of the for..in structure is that it continues to search for enumerable properties up the prototype chain. It will also iterate inherited enumerable properties. It is up to you to verify that the current property exists directly on the local object and not the prototype it is attached to with hasOwnProperty()...
for ( var prop in obj ) {
if ( obj.hasOwnProperty(prop) ) {
// prop is actually obj's property (not inherited)
}
}
(More on Prototypal Inheritance)
The problem with using the for..in structure on the Array type is that there is no garauntee as to what order the properties are produced... and generally speaking that is a farily important feature in processing an array.
Another problem is that it usually slower than a standard for implementation.
Bottom Line
Using a for...in to iterate arrays is like using the butt of a screw driver to drive a nail... why wouldn't you just use a hammer (for)?
for...in is to be used when you want to loop over the properties of an object. But it works the same as a normal for loop: The loop variable contains the current "index", meaning the property of the object and not the value.
To iterate over arrays, you should use a normal for loop. buttons is not an array but a NodeList (an array like structure).
If iterate over buttons with for...in with:
for(var i in a) {
console.log(i)
}
You will see that it output something like:
1
2
...
length
item
because length and item are two properties of an object of type NodeList. So if you'd naively use for..in, you would try to access buttons['length'].removeAttribute() which will throw an error as buttons['length'] is a function and not a DOM element.
So the correct way is to use a normal for loop. But there is another issue:
NodeLists are live, meaning whenever you access e.g. length, the list is updated (the elements are searched again). Therefore you should avoid unnecessary calls to length.
Example:
for(var i = 0, l = buttons.length; i < l, i++)
for(var key in obj) { } iterates over all elements in the object, including those of its prototypes.
So if you are using it and cannot know nothing extended Object.prototype you should always test obj.hasOwnProperty(key) and skip the key if this check returns false.
for(start; continuation; loop) is a C-style loop: start is executed before the loop, continuation is tested and the loop only continues while it's true, loop is executed after every loop.
While for..in should not generally be used for Arrays, however prior to ES5 there was a case for using it with sparse arrays.
As noted in other answers, the primary issues with for..in and Arrays are:
The properties are not necessarily returned in order (i.e. not 0, 1, 2 etc.)
All enumerable properties are returned, including the non–index properties and those on the [[Prototype]] chain. This leads to lower performance as a hasOwnProperty test is probably required to avoid inherited properties.
One reason to use for..in prior to ES5 was to improve performance with sparse arrays, provided order doesn't matter. For example, in the following:
var a = [0];
a[1000] = 1;
Iterating over a using for..in will be much faster than using a for loop, as it will only visit two properties whereas a for loop will try 1001.
However, this case is made redundant by ES5's forEach, which only visits members that exist, so:
a.forEach();
will also only iterate over two properties, in order.