background

Array traversal is certainly familiar in JavaScript, and the two most common ones are forEach and Map.

(There are, of course, others such as for, for in, for of, reduce, filter, every, some…)

The reason I wrote this a few days ago is because I made a stupid and stupid mistake while writing code a few days ago, and I ended up with a small bug.

Finally find the reason, angry, even a little want to laugh, today write about these two methods.

Comparison and conclusion

ForEach: executes provided functions forEach element.

Map: Creates a new array in which each element is derived by calling the provided function to execute each element in the array.

Let’s get straight to the conclusion:

The forEach method does not return the result of execution, but undefined.

That is, forEach will modify the original array, and the map method will get a new array and return it.

Now let’s look at a concrete example.

forEach

The forEach method performs a callback forEach item in the array that has a valid value, in ascending order. Deleted or uninitialized items are skipped (for example, on a sparse array).

ForEach receives two arguments: arr. ForEach (callback[, thisArg]);

The callback function is passed three arguments in turn:

  1. The value of the current item in the array
  2. The index of the current item in the array
  3. The array object itself

Such as:

const arr = ['1', '2', '3']; // callback function takes 3 parameters // the current value of an array as the first parameter // the position of the current value in an array as the second parameter // the original source array as the third parameter const cb = (str, i, origin) => { console.log(`${i}: ${Number(str)} / ${origin}`); }; arr.forEach(cb); // 0: 1/1,2,3 // 1: 2/1,2,3 // 2: 3/1,2,3Copy the code

If thisArg has a value, this will point to the thisArg object every time the callback function is called.

If thisArg is omitted, or if null or undefined is assigned, this refers to the global object.

To take a reluctant example, update an object’s property from the value of the element in each array:

function Counter() { this.sum = 0; this.count = 0; } Counter.prototype.add = function(array) { array.forEach(function(entry) { this.sum += entry; this.count++; }, this); // console.log(this) -> Counter }; var obj = new Counter(); obj.add([1, 3, 5, 7]); obj.count; // 4 obj.sum; / / 16Copy the code

This arg argument is ignored if you pass in function arguments using the arrow function because the arrow function is lexically bound to this.

In general, only the first two parameters of callback are used.

map

The map does the same thing as the for loop, except that the map creates a new array.

So, if you still use a map when you don’t intend to use the new array returned, it defeats the purpose of the map.

The map function takes two arguments:

  1. Callback Callback also takes three arguments:

    1. currentValue
    2. index
    3. array
  2. ThisArg (optional)

This is similar to forEach.

Specific examples:

const arr = ['1', '2', '3']; // callback function takes 3 parameters // the current value of an array as the first parameter // the position of the current value in an array as the second parameter // the original source array as the third parameter const cb = (str, i, origin) => { console.log(`${i}: ${Number(str)} / ${origin}`); }; arr.map(cb); // 0: 1/1,2,3 // 1: 2/1,2,3 // 2: 3/1,2,3Copy the code

Callback method use case:

arr.map((str) => { console.log(Number(str)); })
Copy the code

The result of the map is a new array that is not equal to the original array:

const arr = [1];
const new_arr = arr.map(d => d);
arr === new_arr; // false
Copy the code

You can also specify the value of the second argument thisArg:

const obj = { name: 'Jane' };

[1].map(function() {
  console.dir(this); // { name: 'Jane' }
}, obj); 
Copy the code

But if you use the arrow function, this will be window:

[1].map(() => {
  console.dir(this);   // window
}, obj);
Copy the code

Arrow functions and normal functions work differently, and you can read this article for details.

www.geeksforgeeks.org…

Write here, think of a classic topic.

A case study of using skills

[“1”, “2”, “3”].map(parseInt);

We’ve all seen this before. We expect the output [1, 2, 3], and the actual result is [1, NaN, NaN].

Let’s briefly analyze the process. First, let’s take a look at parameter transfer:

// parseInt(string, radix) -> map(parseInt(value, index)); // 1 second iteration (index is 1): parseInt("2", 1); // The third iteration of NaN (index is 2): parseInt("3", 2); //NaNCopy the code

Seeing this, a solution was in sight:

function returnInt(element) { return parseInt(element, 10); } ['1', '2', '3'].map(returnInt); / / [1, 2, 3]Copy the code

Is it easy to understand?

Back to business.

The difference between forEach and map

Take a look at two lines of code:

[1,2,3]. Map (d => d + 1); / / [2, 3, 4]; [1,2,3]. ForEach (d => d + 1); // undefined;Copy the code

The author of Vue, the big man of Rain Creek, has this graphic analogy:

Foreach is when you do something with them, one by one, in any order:

people.forEach(function (dude) {
  dude.pickUpSoap();
});
Copy the code

A map is when you take a box (a new array) and tell them to drop their wallets in it one by one. You end up with a new array of people’s wallets in the same order as their wallets.

var wallets = people.map(function (dude) {
  return dude.wallet;
});
Copy the code

“Reduce” means you take your wallet, count it one by one and see how much money is in it. Each time you check one, you add it to the previous sum. So at the end of the day you know how much money everybody has. var totalMoney = wallets.reduce(function (countedMoney, wallet) { return countedMoney + wallet.money; }, 0);

Very apt.

Topic link: www.zhihu.com/questio…

By the way, here’s a simple native implementation to feel the difference:

Array.prototype.map = function (fn) {
    var resultArray = [];
    for (var i = 0,len = this.length; i < len ; i++) {
         resultArray[i] = fn.apply(this,[this[i],i,this]);
    }
    return resultArray;
}

Array.prototype.forEach = function (fn) {
    for (var i = 0,len = this.length; i < len ; i++) {
         fn.apply(this,[this[i],i,this]);
    }
}

Array.prototype.reduce= function (fn) {
    var formerResult = this[0];
    for (var i = 1,len = this.length; i < len ; i++) {
         formerResult = fn.apply(this,[formerResult,this[i],i,this]);
    }
    return formerResult;
}
Copy the code

Very simple implementation, only to achieve the function, did not do fault tolerance and particularly strict context processing.

When to use Map and forEach

Since the main difference between the two is whether a value is returned, map is used for generating new arrays, and forEach is used for other arrays.

In React, maps are also used to iterate over data-generated elements:

const people = [
  { name: 'Josh', whatCanDo: 'painting' },
  { name: 'Lay', whatCanDo: 'security' },
  { name: 'Ralph', whatCanDo: 'cleaning' }
];

function makeWorkers(people) {
  return people.map((person) => {
    const { name, whatCanDo } = person;
    return <li key={name}>My name is {name}, I can do {whatCanDo}</li>
  });
}

<ul> {makeWorkers(people)}</ul>
Copy the code

When you don’t need to create a new book group, use forEach:

const mySubjectId = ['154', '773', '245'];

function countSubjects(subjects) {
  let count = 0;
  
  subjects.forEach(subject => {
    if (mySubjectId.includes(subject.id)) {
      count += 1;
    }
  });
  
  return count;
}

const countNumber = countSubjects([
  { id: '223', teacher: 'Mark' },
  { id: '154', teacher: 'Linda' }
]);

countNumber; // 1
Copy the code

This code can also be abbreviated as:

subjects.filter(subject => mySubjectId.includes(subject.id)).length;
Copy the code

conclusion

Having said a lot, I believe you must have a clearer understanding of these two methods. Let’s review and conclude:

ForEach modifies the original array, and the map method takes a new array and returns it.

Who’s faster? In fact, we don’t have to worry about which is fast, anyway, it’s not as fast as for.

Readability is what we need to consider. So when you need to generate a new array, use map, otherwise use forEach.