Talk about the differences between loops in JavaScript in detail

  • 2021-11-13 00:42:44
  • OfStack

Enumerable properties of directory preface Iterable object
forEach and map methods Chain call
Performance
Conclusion

Preface

When using loops in JavaScript, you need to define two key things correctly: enumerable properties (enumerable properties) and iterative objects (iterable objects).

Enumerable properties

One defining feature of an enumerable object is that when we assign a property to the object through the assignment operator, we set the internal enumerable flag (enumerable) to true. This is the default value.

However, we can change this behavior by setting it to false.

The rule of thumb is that enumerable attributes always appear in the for... in loop.

Let's look at this one point:


const users = {}
users.languages = 'JavaScript'
​
Object.getOwnPropertyDescriptor(users, 'languages')
// output -> { value: 'JavaScript', writable: true, enumerable: true, configurable: true }
​
//  More control over the attributes we use in the loop 
Object.defineProperty(users, 'role', { value: 'Admin', writable: true, enumerable: false })
​
for (const item in users) {
  console.log(item) // languages
}

As you can see, we added an languages attribute to the users variable, and the enumerable attribute of the languages attribute descriptor is true using the Object. getOwnPropertyDescriptor method.

Using Object. defineProperty to add the role attribute and setting enumerable to false, the role attribute is not output in the for... in loop. That is, the properties in the for... in loop are enumerable properties.

Iterable object

An object is iterative if it defines its iterative behavior. In this example, the value that will be looped in the for... of construct will define its iterative behavior. Iterable built-in types including Array, String, Set, and Map objects are not iterable because it does not specify the @ iterator method.

Basically, in JavaScript, all iterative objects are enumerable objects, but not all enumerable objects are iterative objects.

Here is a conceptual approach: for... in looks for objects in the data, and for... of looks for repeats.

Let's look at the effect of this 1 cut when used with Array data type 1:


const languages = ['JavaScript', 'Python', 'Go']
​
//  And  for...in  Cycle 1 Start using 
for (const language in languages) {
  console.log(language)
}
// output
// 0
// 1
// 2
​
//  And  for...of  Cycle 1 Start using 
for (const language of languages) {
  console.log(author)
}
// output -> JavaScript Python Go

One thing to keep in mind when using this construct is that if typeof is called and the output is object, you can use the for... in loop.

Let's look at this operation on the languages variable:


typeof languages // "object" ->  So we can use the  for

At first glance, this may seem surprising, but it should be noted that an array is a special type of object with an index as its key. Knowing that for... in will look for objects in constructs can help us enormously. When the for... in loop finds an object, it loops over each key.

We can visualize the way for... in loops over an languages array as follows:


const languages = {
  0: 'JavaScript',
  1: 'Python',
  2: 'Go'
}

Note: If it can be traced to 1 object (or inherited from the object prototype chain), for... in will traverse the keys in no particular order.

Also, if it implements one iterator for.. of construct, it loops through the value in each iteration.

forEach and map methods

Although the forEach and map methods can be used to achieve the same goal, their behavior and performance characteristics differ.

At the basic level, when functions are called, they all receive 1 callback as an argument.

Consider the following fragment:


const scoresEach = [2, 4 ,8, 16, 32]
const scoresMap = [2, 4 ,8, 16, 32]
const square = (num) => num * num

Let's introduce some differences in their operation in detail.

forEach returns undefined and map returns a new array:


let newScores = []
const resultWithEach = scoresEach.forEach(score => {
  const newScore = square(score)
  newScores.push(newScore)
})
​
const resultWithMap = scoresMap.map(square)
​
console.log(resultWithEach) // undefined
console.log(resultWithMap) // [4, 16, 64, 256, 1024]

Map is a pure function, while forEach performs one mutation:


console.log(newScores) // [4, 16, 64, 256, 1024]

In my opinion, map supports functional programming paradigm. We don't have to always perform mutations to get the desired results, unlike forEach, we have to mutate newScores variables. At each run, the map function produces the same result when the same input is supplied. At the same time, the forEach corresponding term will be extracted from the previous value of the previous mutation.

Chain call

Using map, you can make a chain call because the result returned is an array. Therefore, any other array method can be called immediately on the result. In other words, we can call filter, reduce, some, and so on. This is not possible in forEach because the value returned is undefined's.

Performance

The performance of map method is often better than that of forEach method.

Check the performance of equivalent code blocks implemented using map and forEach. On average, you will see that the map function executes at least 50% faster.

Conclusion

Of all the loop structures discussed above, the one that gives us the most control is the for... of loop. We can use it with the keywords return, continue, and break 1. This means that we can specify what happens to each element in the array and whether to leave or skip early.


Related articles: