I am iterating over an array in MooTools but seeing additional items when iterating through the array using the shorthand for..in loop. It works fine when I use the regular for loop. Is this a problem with MooTools polluting the global namespace or am I doing something wrong here?
There is a createTabs() function that iterates over an array and creates a tab for each value in the array:
function createTabs() {
var myTabs = [["First", "a.png"], ["Second", "b.png"]];
for(var i in myTabs) {
var tab = new Tab(myTabs[i][0], myTabs[i][1]);
console.log(i);
}
}
This is the output of console.log(i):
0
1
$family
each
clean
associate
link
contains
extend
getLast
getRandom
include
combine
erase
empty
flatten
hexToRgb
rgbToHex
toJSON
I understand the first 2 indexes, but where is the rest coming from?
Edit: Thanks for the quick answers Chetan and k Prime. That makes sense, and the Array.each addition by MooTools is much cleaner way to iterate!
Looks a lot better now:
myTabs.each(function(item) {
var tab = new Tab(item[0], item[1]);
console.log(item);
});
As Chetan pointed out, for .. in is meant for object property iteration, not arrays. however, you can iterate over the current members (and not the inherited members set by MooTools), by using hasOwnProprty, like so:
for (i in array)
if (array.hasOwnProperty(i))
{
//.. do stuff ...
}
Orr, better still, since you're using MooTools, just use the Array.each method:
array.each (function (item, index)
{
// ... do stuff ...
});
for..in is not meant for array iteration. It iterates over all the properties of an object that are not built-in. Since MooTools has added more functions to Array prototype, they are now array properties as well. See this https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Statements/For...in
Just use a basic for loop for array iteration.
Related
I was looking at some snippets of code, and I found multiple elements calling a function over a node list with a forEach applied to an empty array.
For example I have something like:
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
but I can't understand how it works. Can anyone explain me the behaviour of the empty array in front of the forEach and how the call works?
[] is an array.
This array isn't used at all.
It's being put on the page, because using an array gives you access to array prototypes, like .forEach.
This is just faster than typing Array.prototype.forEach.call(...);
Next, forEach is a function which takes a function as an input...
[1,2,3].forEach(function (num) { console.log(num); });
...and for each element in this (where this is array-like, in that it has a length and you can access its parts like this[1]) it will pass three things:
the element in the array
the index of the element (third element would pass 2)
a reference to the array
Lastly, .call is a prototype which functions have (it's a function which gets called on other functions).
.call will take its first argument and replace this inside of the regular function with whatever you passed call, as the first argument (undefined or null will use window in everyday JS, or will be whatever you passed, if in "strict-mode"). The rest of the arguments will be passed to the original function.
[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
console.log(i + ": " + item);
});
// 0: "a"
// 1: "b"
// 2: "c"
Therefore, you're creating a quick way to call the forEach function, and you're changing this from the empty array to a list of all <a> tags, and for each <a> in-order, you are calling the function provided.
EDIT
Logical Conclusion / Cleanup
Below, there's a link to an article suggesting that we scrap attempts at functional programming, and stick to manual, inline looping, every time, because this solution is hack-ish and unsightly.
I'd say that while .forEach is less helpful than its counterparts, .map(transformer), .filter(predicate), .reduce(combiner, initialValue), it still serves purposes when all you really want to do is modify the outside world (not the array), n-times, while having access to either arr[i] or i.
So how do we deal with the disparity, as Motto is clearly a talented and knowledgeable guy, and I would like to imagine that I know what I'm doing/where I'm going (now and then... ...other times it's head-first learning)?
The answer is actually quite simple, and something Uncle Bob and Sir Crockford would both facepalm, due to the oversight:
clean it up.
function toArray (arrLike) { // or asArray(), or array(), or *whatever*
return [].slice.call(arrLike);
}
var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);
Now, if you're questioning whether you need to do this, yourself, the answer may well be no...
This exact thing is done by... ...every(?) library with higher-order features these days.
If you're using lodash or underscore or even jQuery, they're all going to have a way of taking a set of elements, and performing an action n-times.
If you aren't using such a thing, then by all means, write your own.
lib.array = (arrLike, start, end) => [].slice.call(arrLike, start, end);
lib.extend = function (subject) {
var others = lib.array(arguments, 1);
return others.reduce(appendKeys, subject);
};
Update for ES6(ES2015) and Beyond
Not only is a slice( )/array( )/etc helper method going to make life easier for people who want to use lists just like they use arrays (as they should), but for the people who have the luxury of operating in ES6+ browsers of the relatively-near future, or of "transpiling" in Babel today, you have language features built in, which make this type of thing unnecessary.
function countArgs (...allArgs) {
return allArgs.length;
}
function logArgs (...allArgs) {
return allArgs.forEach(arg => console.log(arg));
}
function extend (subject, ...others) { /* return ... */ }
var nodeArray = [ ...nodeList1, ...nodeList2 ];
Super-clean, and very useful.
Look up the Rest and Spread operators; try them out at the BabelJS site; if your tech stack is in order, use them in production with Babel and a build step.
There's no good reason not to be able to use the transform from non-array into array... ...just don't make a mess of your code doing nothing but pasting that same ugly line, everywhere.
The querySelectorAll method returns a NodeList, which is similar to an array, but it's not quite an array. Therefore, it doesn't have a forEach method (which array objects inherit via Array.prototype).
Since a NodeList is similar to an array, array methods will actually work on it, so by using [].forEach.call you are invoking the Array.prototype.forEach method in the context of the NodeList, as if you had been able to simply do yourNodeList.forEach(/*...*/).
Note that the empty array literal is just a shortcut to the expanded version, which you will probably see quite often too:
Array.prototype.forEach.call(/*...*/);
The other answers have explained this code very well, so I'll just add a suggestion.
This is a good example of code that should be refactored for simplicity and clarity. Instead of using [].forEach.call() or Array.prototype.forEach.call() every time you do this, make a simple function out of it:
function forEach( list, callback ) {
Array.prototype.forEach.call( list, callback );
}
Now you can call this function instead of the more complicated and obscure code:
forEach( document.querySelectorAll('a'), function( el ) {
// whatever with the current node
});
It can be better written using
Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {
});
What is does is document.querySelectorAll('a') returns an object similar to an array, but it does not inherit from the Array type.
So we calls the forEach method from the Array.prototype object with the context as the value returned by document.querySelectorAll('a')
[].forEach.call( document.querySelectorAll('a'), function(el) {
// whatever with the current node
});
It is basically the same as:
var arr = document.querySelectorAll('a');
arr.forEach(function(el) {
// whatever with the current node
});
Want to update on this old question:
The reason to use [].foreach.call() to loop through elements in the modern browsers is mostly over. We can use document.querySelectorAll("a").foreach() directly.
NodeList objects are collections of nodes, usually returned by
properties such as Node.childNodes and methods such as
document.querySelectorAll().
Although NodeList is not an Array, it is possible to iterate over it
with forEach(). It can also be converted to a real Array using
Array.from().
However, some older browsers have not implemented NodeList.forEach()
nor Array.from(). This can be circumvented by using
Array.prototype.forEach() — see this document's Example.
Lots of good info on this page (see answer+answer+comment), but I recently had the same question as the OP, and it took some digging to get the whole picture. So, here's a short version:
The goal is to use Array methods on an array-like NodeList that doesn't have those methods itself.
An older pattern co-opted Array's methods via Function.call(), and used an array literal ([]) rather than than Array.prototype because it was shorter to type:
[].forEach.call(document.querySelectorAll('a'), a => {})
A newer pattern (post ECMAScript 2015) is to use Array.from():
Array.from(document.querySelectorAll('a')).forEach(a => {})
An empty array has a property forEach in its prototype which is a Function object. (The empty array is just an easy way to obtain a reference to the forEach function that all Array objects have.) Function objects, in turn, have a call property which is also a function. When you invoke a Function's call function, it runs the function with the given arguments. The first argument becomes this in the called function.
You can find documentation for the call function here. Documentation for forEach is here.
Just add one line:
NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
And voila!
document.querySelectorAll('a').forEach(function(el) {
// whatever with the current node
});
Enjoy :—)
Warning: NodeList is a global class. Don't use this recomendation if you writing public library. However it's very convenient way for increasing self-efficacy when you work on website or node.js app.
Just a quick and dirty solution I always end up using. I wouldn't touch prototypes, just as good practice. Of course, there are a lot of ways to make this better, but you get the idea.
const forEach = (array, callback) => {
if (!array || !array.length || !callback) return
for (var i = 0; i < array.length; i++) {
callback(array[i], i);
}
}
forEach(document.querySelectorAll('.a-class'), (item, index) => {
console.log(`Item: ${item}, index: ${index}`);
});
[] always returns a new array, it is equivalent to new Array() but is guaranteed to return an array because Array could be overwritten by the user whereas [] can not. So this is a safe way to get the prototype of Array, then as described, call is used to execute the function on the arraylike nodelist (this).
Calls a function with a given this value and arguments provided
individually. mdn
Norguard explained WHAT [].forEach.call() does and James Allardice WHY we do it: because querySelectorAll returns a NodeList that doesn't have a forEach method...
Unless you have modern browser like Chrome 51+, Firefox 50+, Opera 38, Safari 10.
If not you can add a Polyfill:
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = function (callback, thisArg) {
thisArg = thisArg || window;
for (var i = 0; i < this.length; i++) {
callback.call(thisArg, this[i], i, this);
}
};
}
let's say you have : const myList= document.querySelectorAll("p");
This will return an list/array of all in your HTML.
Now Array.prototype.forEach.call(myList, myCallback)
is equivalent to [].forEach.call(myList, myCallback)
where 'myCallback' is a callback function.
You are basically running the callback function on each element of myList.
Hope this helped you!
I don't know if there is any restriction, but it works.
I turned the nodeList into an iterator object using the spread operator and mapped it:
let _btns = document.querySelectorAll('.btn');
[..._btns].map(function(elem, i) {
elem.addEventListener('click', function (e) {
console.log(elem.textContent);
})
})
.btn {
padding: 5px;
color:#fff;
background-color: darkred;
text-align:center;
color: white;
}
<button class="btn">button 1</button>
<button class="btn">button 2</button>
So what is the difference between this two functions?
They both create new Array object. Only difference I found so far is that Array.from supports ArrayLike parameters. I don't see any reason why they just haven't added ArrayLike support for Array.prototype.map function.
Am I missing something?
The purpose of Array.from() is to take a non-array (but array-like) object and make a copy of it into an actual array. This then allows you to use ALL array methods on the copy including things beyond just iterating it such as .splice(), .sort(), .push(), .pop(), etc... which is obviously much more capable than just make .map() work with array-like things.
Array.map seems to be a bit more performant as well:
var a = () => [{"count": 3},{"count": 4},{"count": 5}].map(item => item.count);
var b = () => Array.from([{"count": 3},{"count": 4},{"count": 5}], x => x.count);
var iterations = 1000000;
console.time('Function #1');
for(var i = 0; i < iterations; i++ ){
b();
};
console.timeEnd('Function #1')
console.time('Function #2');
for(var i = 0; i < iterations; i++ ){
a();
};
console.timeEnd('Function #2')
Running this code using Chrome (Version 65.0.3325.181) on this page gave me the follow results:
Function #1: 520.591064453125ms
Function #2: 33.622802734375ms
Static method vs instance method
I know a lot of time has passed since the question was asked. A lot of good things have been said. But I would like to add some more. If we try to determine the nature of the two methods we can say that Array.from
has no relation to any instance of Array. It is static method like Array.isArray or Array.of. You also have static properties like length for the Array object. As a static method Array.from can not be Called from instance.
For example:
var indexes=[0,1,2,3]
index.from()
>>> index.from is not a function
In the other hand if you write
Array.map() you will end up with a Array.map is not a function. It is because Array.prototype.map Exist for the instance of array. In our little example indexes is an instance of Array then we use map on it.
Example
var indexes=[0,1,2,3]
function doubleIt(x){
return 2*x;
}
indexes.map(doubleIt);
With array.from it shoud be something like
Array.from(indexes, doubleIt)
I used quokka plugin on vscode to evaluate performance on vs code in a windows machine. It is not real case of performance benchmarking. But it can help to have an idea. I came up with the same conclusion as #rileynet map seem more performant but only for large array.
var N=10
var tabIndex=[ ...Array(N).keys()]
function doubleIt(x){
return 2*x;
}
tabIndex.map(doubleIt);/*?.*/ 0.040ms
Array.from(tabIndex, doubleIt)/*?.*/ 0.009ms
if N=100
tabIndex.map(doubleIt);/*?.*/ 0.052ms
Array.from(tabIndex, doubleIt)/*?.*/ 0.041ms
if N=1000
tabIndex.map(doubleIt);/*?.*/ 0.228ms
Array.from(tabIndex, doubleIt)/*?.*/ 0.339ms
if N=10000
tabIndex.map(doubleIt);/*?.*/ 2.662ms
Array.from(tabIndex, doubleIt)/*?.*/ 1.847ms
N=100000
tabIndex.map(doubleIt);/*?.*/ 3.538ms
Array.from(tabIndex, doubleIt)/*?.*/ 11.742ms
Making Array.prototype the prototype object for every single array-like "Class" in JS (more importantly, in DOM, where most of the 'array-like' objects live) would be a potential mistake.
What would a .reduce( ) on a list of HTML elements/attributes look like?
Array.from is the official version of [].slice.call(arrayLike); with the added benefit of not having to create an unused array, just to create an array.
So really, Array.from can be polyfilled with function (arrLike) { return [].slice.call(arrLike); }, and minus native-implementation speed/memory improvements, it's the same result.
This has little to do with map|reduce|filter|some|every|find, which are the keys to living a long and happy life, without the need of micromanaging loops to get things done.
I have many functional methods i need to use it and i need to publish a library with this methods to share it with JavaScript developers it helps very much so for instance i need to add a Method named duplicates will return to me the duplicates of the Array
as you can see this method is not officially published by ECMA so i dont know the best form to put the script
1-
Array.prototype.duplicate = function (){
//script here its usefull to use `this` refer to the Array
}
Using it like
[1,2,2].duplicates();
2-
var Ary = function(a){
if(!(this instanceOf Ary))
return new Ary(a)
if(Object.prototype.toString.call(a) != '[object Array]')
return new Error(a + 'is not an Array')
else
{
for(var i =0 ; i<a.length; i++)
{
this.push(a[i]);
}
}
}
Ary.prototype = new Array();
Ary.prototype.constructor = Ary;
Ary.prototype.duplicates = function(){
//script here its usefull to use `this` refer to the Array
};
Using it like
Ary([1,2,2]).duplicates();
i need to know is it more like it to use prototype directly to Array JavaScript Class to add functionality if it is not officialy published with ECMA and instead we do inherit from Array Class and then play with it ???
or its ok do prototype it ??
and whats the consequences
Regards
For your own code, it's fine to add a duplicates method to Array.prototype but you do need to be prepared for what may happen if you use code (either your own, or something you're using) that incorrectly uses for..in to loop through arrays like this:
for (var i in myArray) { // <==== Wrong without safeguards
}
...because i will get the value "duplicates" at some point, since for..in loops through the enumerable properties of an object and its prototype(s), it does not loop through array indexes. It's fine to use for..in on arrays if you handle it correctly, more in this other answer on SO.
If you're only going to work in an ES5-enabled environment (modern browsers, not IE8 and earlier), you can avoid that by adding your duplicates via Object.defineProperty, like this:
Object.defineProperty(Array.prototype, "duplicates", {
value: function() {
// ...the code for 'duplicates' here
}
});
A property defined that way is not enumerable, and so does not show up in for..in loops, so code that fails to correctly handle for..in on arrays isn't impacted.
Unfortunately, it's currenty impossible in JavaScript to correctly derive from Array.prototype (your second option), because Array has special handling of properties whose names are all digits (called "array indexes") and a special length property. Neither of these can currently be correctly provided in a derived object. More about those special properties in my blog article A Myth of Arrays.
As a general rule: don't monkey patch the native Javascript object prototypes. It may appear harmless, but if you're including third party code in your site/application, it can cause all kinds of subtle bugs.
Modifying Array prototype is particularly evil, because the internet is rife with buggy, incorrect code that iterates arrays using the for ... in construct.
Check it out:
for(var i in [1,2,3]) {
console.log(i);
}
Outputs:
1
2
3
But if you've modified the Array prototype as follows:
Array.prototype.duplicates = function() { }
It outputs
1
2
3
duplicates
See for yourself.
I'm wanting to add a prototyped function to javascript's array object.
The problem I'm having is that when I step the keys of an array, the prototyped function's name appears in the array's keys.
Here's a sample to illustrate the problem:
<html>
<head>
<script type="text/javascript">
Array.prototype.foo = function () {
// do something
}
function keys() {
var fruit = ["apples","pears","bannanas"];
for (key in fruit) alert(key); // alerts: 0, 1, 2, foo
}
</script>
</head>
<body onload="keys();">
</body>
</html>
Any ideas on how I can get around this?
I don't have to prototype the function but, from a readability point of view, I'd prefer to.
EDIT: Just to be clear, the real world implementation of the above example iterates through an associative array, hence the for..in loop
EDIT: Thanks for the replies guys but I think you're missing what I'm after. What I'm wanting to know is whether I can add a prototyped function to javascript's array object (or declare the function in a different way that would have the same effect) without having the function name pitch up while iterating through an array via a for..in loop. Take the indexOf function for example. Javascript seems to define it internally in such a way that it doesn't appear in the keys of the object/array (right?). Can this same behaviour be reproduced at runtime?
You need to check whether fruit.hasOwnProperty(key) in the loop.
To avoid inherited enumerable properties with for..in, use a hasOwnProperty test:
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
// p is a property of obj and not inherited
}
}
Using for..in with arrays is not recommended - if you want an object, use an Object.
Arrays are normally used where the members are accessed in order, e.g. using a counter in a loop. Using for..in the order in which properties are returned is implementation dependant and differs between browsers, so you can't guarantee order.
As James says use rather than...
function keys() {
var fruit = ["apples", "pairs", "bananas"];
for(var key in fruit) {
alert(key);
}
}
Instead do the following...
function keys() {
var fruit = ["apples", "pairs", "bananas"];
for(var i = 0; i < fruit.length; ++i) {
alert(i);
}
}
As the length property will only update with the indexed array items and not items added to the prototype.
To iterate trough an associative array with for in and get what you want i would use typeof to type-check the propriety and restrict to non-functions. You can use
number,
string,
boolean,
object,
function
and undefined
with typeof
http://msdn.microsoft.com/en-us/library/259s7zc1%28v=vs.94%29.aspx
I appreciate it isn't always available but you can avoid the whole mess by using Array#forEach or if you're using a JavaScript library (quite likely and highly recommended) then you have something like Prototype's Enumerable#each(), Sugar's Array#each() or jQuery's $.each().
anyone can explain how to use for...in statement in javascript. I had read the w3school article but i think it is not so clear.Below is the code, please explain this:
<html>
<body>
<script type="text/javascript">
var x;
var mycars = new Array();
mycars[10] = "Saab";
mycars[20] = "Volvo";
mycars[30] = "BMW";
for (x in mycars)
{
document.write(mycars[x] + "<br />");
}
</script>
</body>
</html>
A for in loop will iterate through every property in an object.
In your example, the x variable will cycle through every property in the mycars object.
If you add mycars.expensive = "Porsche";, it will find that too.
Note that, as stated by MDC, for in loops should not be used to loop through ordinary arrays:
Although it may be tempting to use
this as a way to iterate over an Array,
this is a bad idea. The
for...in statement
iterates over user-defined properties
in addition to the array elements, so
if you modify the array's non-integer
or non-positive properties (e.g. by
adding a "foo" property
to it or even by adding a method or
property to
Array.prototype), the
for...in statement will
return the name of your user-defined
properties in addition to the numeric
indexes. Also, because order of
iteration is arbitrary, iterating over
an array may not visit elements in
numeric order. Thus it is better to
use a traditional for loop with a numeric index when
iterating over arrays. Similar
arguments might be used against even
using for...in at all (at least
without propertyIsEnumerable()
or hasOwnProperty()
checks), since it will also iterate
over Object.prototype (which, though
usually discouraged, can, as in the
case of Array.prototype, be usefully
extended by the user where are no
namespacing concerns caused by
inclusion of other libraries which
might not perform the above checks on
such iterations and where they are
aware of the effect such extension
will have on their own use of
iterators such as for...in).
First you create an object with 3 items (and not an Array)
var mycars = new Object();
mycars[10] = "Saab";
mycars[20] = "Volvo";
mycars[30] = "BMW";
where 10, 20 and 30 are the object properties.
then you want to navigate through the object, visit all properties and display each value associated to a property.
This is where the [ for (variable in object) expression ] javascript construction intervenes:
The variable will be set to the first property of the object, then to the 2nd, then to the last. Try
for (v in mycars) alert(v);
to see how it works, and this as well
for (v in mycars) alert("Property: "+v+", value: "+mycars[v]);
The for ... in construction iterates over every element within the object on the right side of in. In your case, the block below the for statement is executed once for every car in mycars.
for in is a way of burying bugs for later generations to discover. As has been copiously pointed out, if applied to an Array, it will loop through all the elements of the array, and the length of the array, and whatever other data happens to be attached to the array. Applying it to an ordinary object is better, but you still should still filter the indices through hasOwnProperty.
Better to use a framework-provided function like jQuery's $.each