Learning JavaScript deeply — Understanding forEach

Fredrik Strand Oseberg
6 min readJun 4, 2017

--

One of my biggest pet peeves when it comes to learning to program is the smoke and mirrors. At times when I’m watching tutorials, I feel like I’m watching a magician at work, who after tinkering with and showing me the code, magically ends up with a working product.

Ending up with a great product means nothing, unless I am able to understand it. Abstractions are the enemy when learning to program.

I have been learning JavaScript for three months, and it is not until recently that any tutorial that I watched even touched on the debugger. The debugger is a developer tool that allows you to set a breakpoint in your code, which essentially stops program execution when that function is invoked, and allows you to step through what happens in the function at runtime. Essentially removing all the smoke and mirrors from the process. If you don’t know it yet, learn it, and it will repay itself in spades.

So, at this point I have made it my mission to dissect anything that I don’t understand about JavaScript, until I understand it deeply. Today we’ll have a look at the array method forEach.

But, forEach is explanatory right? It just steps through every item and does something with it. Fair point, but where does it get it’s items from? How does it know which index the item in the array is? How does it know how to execute the callback on every item. I need to know.

MDN to the rescue

The Mozilla Developer Network docs have the following to say about forEach:

The forEach() method executes a provided function once for each array element.

The callback takes three parameters: currentValue, index and array. You can also specify an object to use as this within the function.

arr.forEach(function callback(currentValue, index, array) {
//your iterator
}[, thisArg]);

Ok. So let’s try an example:

[1, 2, 3].forEach(function(number, index) {console.log(number); // 1, 2, 3
console.log(index); // 0, 1, 2
console.log(this.name) // 'I am this'
}, {name: 'I am this'});

So as we can see the forEach function does all that it is required to do. It does something with each item in the array. The forEach method does not mutate the array, but the callback may do so.

So let’s take a peek of what goes on under the hood and try to build our own forEach function. Let’s begin with an empty function definition. We won’t add it to the array prototype in this case, so in our version we will have to give the forEach the array as a parameter.

function forEach(array, callback, thisObj) {}

So our function takes three parameters: (1) the array to operate on, (2) the callback function to run and (3) an optional this object. So let’s keep thinking about this. Is there a way to access each element in an array programatically? Yes, there is! Let’s use a for loop.

function forEach(array, callback, thisObj) {
for (var i = 0; i < array.length; i++) {
callback();
}
}
Example:forEach([1, 2, 3], function(element) {
console.log(element); // undefined
});

So now we’re running the callback specified in the function. In the example we can see that the callback is a simple console.log that we try to run on the element. But since we’re not passing anything into the callback function in our forEach yet, it’s undefined. So to make this work we need to pass in the the “ith” element of the for loops array. I know, it sounds tricky but bear with me.

function forEach(array, callback, thisObj) {
for (var i = 0; i < array.length; i++) {
callback(array[i]); // Setting the ith element of array as first parameter
}
}
forEach([1, 2, 3], function(element) {
console.log(element); // 1, 2, 3
});

Before we continue I want to make sure you’re still with me. So in our forEach method we are looping through the length of the array given as a parameter (so if our array is [1, 2, 3], we’ll loop 3 times). Then, we’re passing the ith element into our callback. So this:

callback(array[i])

With the array [1, 2, 3] given as a parameter to forEach, and console log in the callback function, is equivalent to this:

console.log(array[0]) // 1
console.log(array[1]) // 2
console.log(array[2]) // 3

Next, we’ll configure the function to pass the index of the current element into the callback function.

function forEach(array, callback, thisObj) {
for (var i = 0; i < array.length; i++) {
callback(array[i], i);
}
}
forEach([1, 2, 3], function(element, index) {
console.log(index); // 0, 1, 2
});

This is a simple matter of adding i in the for loop as the second argument to the callback function. Which means that the index parameter in our example will be equivalent to whatever i is on the current iteration. Note that you need to have (element, index) in order for this to work. If you don’t, index will become the element instead, because element is the first parameter to the callback function.

Ok, we’re almost done. But lastly, we’ll add the option to configure what this is inside of the callback function. This takes some wrapping your head around, so let’s walk through it one more time.

Our forEach accepts three parameters: (1) an array, (2) a callback function and (3) a this object.

forEach([1, 2, 3], function(element, index) {
console.log(index); // 0, 1, 2
}, {name: 'I am the this object'});

The above looks pretty messy, so let’s clean it up so it becomes more clear:

var arrayToOperateOn = [1, 2, 3];
var thisObject = {
name: 'I am the this object';
}
forEach(arrayToOperateOn, function() {}, thisObject)

In the second example we extracted the array and the thisObject to their own variables. The last line shows the complete structure of how we call our forEach method with all three parameters, but I left the callback function blank for clarity. So our full function call will look like this:

var arrayToOperateOn = [1, 2, 3];
var thisObject = {
name: 'I am the this object';
}
forEach(arrayToOperateOn, function() {
console.log(this.name); // undefined
}, thisObject)

Here we attempt to access the this.name property in our callback function. But since we haven’t built the functionality yet, we get undefined. So let’s build it!

So let’s think again. How can we set the property of the function to be whatever the thisObject is set to be? Well, as it happens there is a handy method on the function prototype called bind. Bind will essentially return a new function that is set to whatever object you pass as the argument to bind. So with that in mind let’s rewrite our forEach.

function forEach(array, callback, thisObj) {
var forEachCallback = callback; // initialize variable to callback
if (thisObj) { // check if there exists a thisObj
forEachCallback = callback.bind(thisObj); // sets variable to be equal to a new function bound to the value of thisObj
}
for (var i = 0; i < array.length; i++) {
forEachCallback(array[i], i); // call the function
}
}

Phew. Thats a bit more code and my brain is already hurting, but let’s go through it. This is the last part, I promise. So in the beginning of the function we’re setting a new variable to be equal to callback. This variable now holds the intial function.

Then we do a simple if statement to see if thisObj is specified. If it is, we are setting the variable forEachCallback to be equal to callback.bind(thisObj). As we already discussed, bind returns a new function with it’s this value set to whatever you pass as a parameter. So this will create a new function that has it’s this value set to thisObj’s value.

Lastly we modify the callback inside the forloop to use the forEachCallBack as the function to run on each item in the loop. The result is this:

function forEach(array, callback, thisObj) {
var forEachCallback = callback;
if (thisObj) {
forEachCallback = callback.bind(thisObj);
}
for (var i = 0; i < array.length; i++) {
forEachCallback(array[i], i);
}
}
var arrayToOperateOn = [1, 2, 3];
var thisObject = {
name: 'I am the this object';
}
forEach(arrayToOperateOn, function() {
console.log(this.name); // console logs 'I am the this object';
}, thisObject)

So now we can access the thisObject that we specified inside the callback function. Great success!

Thats it! We’ve dissected forEach and learned a great deal in the process.

Now, hopefully you see the value of popping the hood of these functions and seeing how they work underneath. I find that removing all the smoke and mirrors and getting down into the nitty gritty details helps my learning process a great deal.

If you enjoyed this article, please recommend so that I know whether or not to make more deep dives of functions in the future, and follow me if you’d like to learn of all the insights I gain while learning JavaScript.

Happy coding.

--

--

Fredrik Strand Oseberg

Entrepreneur, currently on a journey of learning how to code.