Hi!
About Lodash's forEach function, and Lodash in general…
I told you that it "abstracts away from you the chore (and complexity) of looping", so that you can focus on what really matters for your application: the collection you want to iterate through, and the piece of logic you wish to be applied for each item.
You use forEach like this:
// You first define "the collection you want to iterate through":
var myArray = ["lazy cat", "wild dog", "hidden Lorbo", "Alf", "POTUS"];
// You then define "the piece of logic you wish to be applied for each item":
var myFunction = function(element) {
// do something with the element
};
// And finally, you leverage Lodash#forEach:
_(myArray).forEach(myFunction); // looping chore/complexity abstracted away!myFunction above is just a definition, you are never calling it. At least not explicitly: you merely pass its reference (its name) to the forEach function which will take care, behind the scenes, of looping through the elements of myArray and of calling myFunction on each step of the looping, passing in the current item as element.
Now, how does forEach really work behind the scenes? If you go to https://lodash.com/docs#forEach you'll see three icons at the top: # S N. Clicking on S will show you the source code on GitHub (you could also find it in your local lodash.core.js file, but it's just easier clicking on a button ;)).
It looks like this:
function forEach(collection, iteratee) {
var func = isArray(collection) ? arrayEach : baseEach;
return func(collection, getIteratee(iteratee, 3));
}A quick note:
_(myArray).forEach(myFunction), the way we used it, internally gets transformed into_.forEach(myArray, myFunction), the way it's being defined above with two arguments.myArrayis known as the "collection", andmyFunctionas the "iteratee", aka. "the piece of logic you wish to be applied for each item".
A little bit of context: forEach may process either arrays or hashes (objects), and lodash leverages a different function in each case (for efficiency reasons); therefore it first checks what type of collection it has been provided with. myArray is an array indeed, so isArray(myArray) returns true and forEach stores a reference to the convenient arrayEach function as func. At this point, the second line "looks" like this at runtime:
return arrayEach(myArray, getIteratee(myFunction, 3));You would rather expect something simpler, like arrayEach(myArray, myFunction);, so what's getIteratee all about? Well, that one's a bit complex, so long story short: lodash wants people to have full control over that "iteratee", so it's somewhat customizable. getIteratee will take care of checking whether any customization has been applied globally, and also is able to fallback on a default iteratee shall no valid iteratee be provided. In our specific case, we're not doing anything fancy, so it eventually does call:
arrayEach(myArray, myFunction); // which is pretty close to the initial code, forEach(myArray, myFunction)What's left for us is unveiling what exactly arrayEach does. It's defined at https://github.com/lodash/lodash/blob/4.12.0/lodash.js#L459-L478 (to quickly find it, I looked for "function arrayEach" in my browser, and not just "arrayEach"):
function arrayEach(array, iteratee) {
var index = -1,
length = array.length;
while (++index < length) {
if (iteratee(array[index], index, array) === false) {
break;
}
}
return array;
}A quick glance at it, and you'll notice the while loop. They're using some (nice) tricks (for instance, combining the incrementation with the exit condition for instance (++index < length)), but that sets aside, it looks like a good old while loop! arrayEach takes care of calling myFunction (remember, it's known as iteratee at runtime).
Some may refer to
iteratee/myFunctionas a "callback". As the developer, you simply providedforEachwith a reference to the function using_(myArray).forEach(myFunction), and internally,forEach/arrayEachis "callingmyFunctionback" for you when the time is right.
What's super interesting is the way they are calling the iteratee:
iteratee(array[index], index, array)In our example, at runtime, that evaluates to myFunction(myArray[index], index, myArray). So myFunction is called with three arguments, on each step of the looping:
- the first one is the item at position
indexwithinmyArray - the second one is the
indexposition itself, in case you need it - the third and last one is the entire array itself, in case you need it
By "in case you need it", I meant that you may define myFunction one of those three different ways:
var myFunction = function(item) { … } // #1
var myFunction = function(item, index) { … } // #2
var myFunction = function(item, index, collection) { … } // #3We're using #1 right now, expecting a single argument that's the current item:
var myFunction = function(element) {
// do something with the element
};But depending on what "do something with the element" means, we might need more information that just the current element, so Lodash is nice and pushes that through arguments as well in case we need the intel.
Another interesting aspect of arrayEach is the fact that whatever myFunction returns gets compared to false:
if (iteratee(array[index], index, array) === false) {
break;
}It means that, if you implement myFunction in such a way that it returns false at some point (for a specific item), it will have the side effect of stopping the looping entirely (break;). It's handy when you are, say, looking for something in an array:
// The following function takes care of searching for a specific element
// within an array. It will return true as soon as it finds it, false otherwise.
function search(array, element) {
var found = false; // The element we've not found just yet.
_(array).forEach(function(item) {
if (item == element) {
// We found the element!
// Let's acknowledge that, then break off the looping.
found = true;
return false;
}
});
return found;
}Using it is as simple as this:
var myArray = ["lazy cat", "wild dog", "hidden Lorbo", "Alf", "POTUS"];
search(myArray, "hidden Jeetbo"); // returns false
search(myArray, "hidden Lorbo"); // returns trueExcellent!
Lodash already has something like
search: https://lodash.com/docs#includes
So you can see how you'll be able to leverage helpers such as forEach or includes: either directly, or within your own functions. My search example happens to be already covered by Lodash's includes, so you'd rather use that helper, but as a general-purpose library, Lodash can only do so much. It's then quite likely that at some point, you would either:
- use Lodash's chaining feature to compose Lodash helpers: https://lodash.com/docs#chain
- extend Lodash with your own little helpers: https://lodash.com/docs#mixin (it is a slightly more advanced use-case, so no need for you to spend time studying it, but just remember that Lodash provides you with a default set of helpers, that you may extend)
Let me know if some of that stuff troubles you :)