Skip to main content

· 5 min read
Santanu Panda

Introduction

JavaScript, being a versatile language, provides essential array manipulation methods for efficient programming. Among these, forEach() stands out, allowing developers to iterate over each element in an array seamlessly. In this blog post, we'll explore the intricacies of forEach(), its usage with examples, delve into the process of creating a polyfill for educational insights, and address considerations like error handling.

Understanding Array.forEach()

The forEach() method, a part of the array prototype in JavaScript, executes a provided function once for each array element, in ascending order. The syntax is straightforward:

const array = [1, 2, 3, 4, 5];

array.forEach((element, index) => {
// Your logic here
console.log(`Element at index ${index}: ${element}`);
});

It's important to note that the forEach() method does not return anything, making it ideal for scenarios where you want to perform an operation on each array element without creating a new array.

The Perils of Using await Inside forEach()

One common pitfall developers encounter is attempting to use the await keyword inside a forEach() loop when dealing with asynchronous operations. Unfortunately, this approach doesn't work as expected due to the synchronous nature of forEach(). Each iteration of the loop does not wait for the asynchronous operation to complete, resulting in unexpected behavior.

Consider the following example:

const asyncOperation = async (element) => {
// Simulating an asynchronous operation
return new Promise((resolve) => setTimeout(() => resolve(element * 2), 1000));
};

const array = [1, 2, 3, 4, 5];

array.forEach(async (element) => {
const result = await asyncOperation(element);
console.log(result); // Outputs undefined for all iterations
});

In this case, the console.log(result) statement will output undefined for all iterations, highlighting the issue of using await within forEach().

The Solution: Embrace for...of

To handle asynchronous operations with ease, it's recommended to use the for...of loop, which respects the asynchronous nature of the tasks. Let's revisit the previous example using for...of:

const asyncOperation = async (element) => {
return new Promise((resolve) => setTimeout(() => resolve(element * 2), 1000));
};

const array = [1, 2, 3, 4, 5];

const processAsyncArray = async () => {
for (const element of array) {
const result = await asyncOperation(element);
console.log(result);
}
};

processAsyncArray();

Now, the output will be the expected doubled values of each array element, as the for...of loop handles asynchronous operations gracefully.

Creating a Polyfill for forEach()

While modern JavaScript environments support forEach(), understanding its underlying implementation is insightful. Here's a simple polyfill for educational purposes:

Array.prototype.myForEach = function (callback) {
if (typeof callback !== "function") {
throw new TypeError(`${callback} is not a function`);
}

for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
};

Additional Considerations:

Error Handling:

Unlike the native forEach() method, this polyfill includes basic error handling. If the provided callback is not a function, it throws a TypeError, mimicking the behavior of the native method.

Immutable vs. Mutable Operations:

The polyfill, like the native forEach(), operates directly on the array elements. Modifications made within the callback function, such as altering the values of the array elements, will reflect in the original array.

Callback Context (this Value):

Similar to the native method, the polyfill ensures that the callback function is executed with the correct context (this value), which is the array itself. Comparison to Other Looping Mechanisms:

While forEach() is convenient for certain scenarios, other looping mechanisms like for...of or map() might be more suitable depending on specific requirements.

Now, let's explore an example of using our polyfill:

// Example usage of the polyfill
const array = [1, 2, 3];

array.myForEach((element, index) => {
console.log(`Element at index ${index}: ${element}`);
});

In this example, our custom myForEach method is seamlessly applied to the array, mimicking the behavior of the native forEach() method. This hands-on implementation allows you to appreciate the simplicity of the forEach() method and how it can be recreated with just a few lines of code.

Conclusion

In the world of JavaScript array manipulation, forEach() holds its ground as a powerful and intuitive method for iterating over arrays. While its synchronous nature may pose challenges with asynchronous operations, the for...of loop provides a viable solution. Additionally, creating a polyfill for forEach() sheds light on its inner workings, offering valuable insights into the language.

When implementing a polyfill, consider incorporating error handling mechanisms to enhance robustness and mirror native behavior. As you embark on your JavaScript journey, remember to choose the right tool for the job, whether it's forEach() for straightforward synchronous tasks or alternatives like for...of for more complex scenarios. Understanding the nuances of array manipulation methods is key to becoming a proficient JavaScript developer.

And as always, Happy Coding!

· 7 min read
Santanu Panda

What is a closure ?

JavaScript closures are functions that can access values outside of their own curly braces.

In order to call a function in your code, the JavaScript interpreter needs to know about the function itself and any other data from the surrounding environment that it depends on. Everything needs to be neatly closed up into a box before it can be fed into the machine.

Take for example a pure function that only depends on its own arguments and internal data.

function add(x, y) {
return x + y;
}

What we have here is a fully self-contained closed expression. When it's called it gets pushed onto the call stack where it's executed and its internal data is only kept in memory until it's popped back off the call stack.

But what if that function references data outside of its own scope like from the global environment or an outer function ? That leaves us with an open expression that references other free variables throughout the environment.

function outerFunction() {
let outerVariable = 10;

function innerFunction(x) {
return x + outerVariable;
}
return innerFunction;
}

// Create a closure by assigning the inner function to a variable
let closure = outerFunction();

// Now, the closure can access the outerVariable
// even though it's no longer directly in the scope
let result = closure(5); // This will be 15 (5 + 10)

Now in order for the interpreter to call this function and also know the value of these free variables, it creates a closure to store them in a place in memory where they can be accessed later. That area of memory is called the heap. And unlike the call stack which is short-lived, heap can keep data in memory indefinitely then decide when it needs to get rid of it later with the garbage collector

So a closure is not just a function it's a function combined with its outer state or lexical environment.

We can create a closure by defining an outer function that contains the state, then an inner function that operates on it. The data contained here will not leak out to the surrounding environment. The inner function has access to data defined in the outer function scope but the outer function does not have access to the inner function.

In addition many javascript apis are callback based and you can use closures to create a function factory that takes an argument then returns a brand new function which can then be passed along to other functions that expect a callback.

Example

Let's take a look at one of the most famous javascript trick questions

what does this code log out.


// Famous JavaScript trick question
for (var i = 0; i < 3; i++) {
function log() {
console.log(i);
}
setTimeout(log, 100);
}`

let's go through it line by line first we're declaring a variable i with the var keyword. Then a for loop that will run three times by incrementing that variable. Now inside the for loop is where closures come into play. We define a function log that console logs the global variable i

This is not a pure function because it depends on a variable outside of its scope, therefore creating a closure. Then from there we set up a timeout and pass the log function as the callback.

This queues up a task to execute the log function after 100 milliseconds

So what do you think the output of this code will be ?

We're capturing the i variable in the closure for each iteration of the loop so it would seem like it should log out as 0 1 2.

But if we log it out it actually console logs 3 three times

The Var vs. Let Dilemma

To understand why that happens we also need to understand the difference between var and let. When you use var in a for loop that variable actually gets hoisted up into the parent scope which in this case would be the global scope.

Watch what happens when we change the variable to let we get our original expectation of a console log of 0 1 2.

With var we have a global variable that we're mutating over and over again but with let we're creating a variable that is scoped to the for loop.

In other words it's local to the for loop and can't be accessed outside of it.

// Fixing the issue with let
for (let i = 0; i < 3; i++) {
function log() {
console.log(i);
}
setTimeout(log, 100);
}

Now remember

A closure is the combination of a function and its lexical environment. In the case of let the closure is capturing the log function along with the variable i for each iteration of the loop which would be 0 1 2.

if we didn't have a closure here javascript would allocate that i variable in memory in the call stack and then immediately release it but because we do have a closure it stores that variable in the heap memory so it can be referenced again when that closure is called by the timeout in the future.

But when var is used it's capturing the reference to the global variable. The reason it logs three three times is because the timeout doesn't run until 100 milliseconds later, which is long after that for loop has completed and iterated up to three.

Unveiling the Magic

We can actually examine this behaviour in the browser dev tools by adding a debugger to the closure. if you try to run this code in the browser with the devtools open. it will take you to the sources tab and allow you to inspect the call stack and scope of the function. When let is used you can see we have a block scoped variable named i but when var is used that variable is now in the global scope which changes the way it's captured by the closure

Bonus Content

Now that we are done with closure. Lets also check few different solutions to get 0 1 2 from the previous question.

In the first solution which we already discussed we have used let, instead of var to achieve this.

Solution 1: Use let instead of var

for (let i = 0; i < 3; i++) {
function log() {
console.log(i);
}

setTimeout(log, 100);
}

Using let creates a block-scoped variable, so each iteration of the loop gets its own variable i, and the closure inside setTimeout captures the correct value.

Solution 2: Pass the value of i as a parameter to setTimeout

for (var i = 0; i < 3; i++) {
function log(index) {
console.log(index);
}

setTimeout(log, 100, i);
}

By passing i as a parameter to the log function inside setTimeout, you are creating a separate copy of i for each invocation of log, preventing the closure issue.

Solution 3: Use an IIFE (Immediately Invoked Function Expression)

for (var i = 0; i < 3; i++) {
(function (index) {
setTimeout(function () {
console.log(index);
}, 100);
})(i);
}

An IIFE creates a new scope for each iteration of the loop, allowing you to capture the correct value of i inside the closure.

Solution 4: Use bind to set the context and pass parameters

for (var i = 0; i < 3; i++) {
function log(index) {
console.log(index);
}

setTimeout(log.bind(null, i), 100);
}

The bind method allows you to set the context (null in this case) and also pass parameters to the function. This way, you can ensure that the correct value of i is passed to the log function.

Each of these solutions addresses the closure issue and ensures that the correct value of i is logged in the setTimeout callback i.e. 0 1 2