1. 程式人生 > 實用技巧 >js中forEach & map

js中forEach & map

背景

JavaScript中,陣列的遍歷我們肯定都不陌生,最常見的兩個便是forEach 和 map。

(當然還有別的譬如for, for in, for of, reduce, filter, every, some, ...)

之所以幾天要寫這個, 是因為前幾天寫程式碼的時候犯了一個低階且愚蠢的錯誤, 最後搞出了個小bug。

最後找到原因, 生氣, 甚至還有點想笑, 今天就寫一下這兩個方法。

正文

我撲街的程式碼是這樣的, 要給一個數組中的物件加一個屬性, 我隨手就寫了如下程式碼:

// Add input_quantity into every list item
const dataSoruceAdapter = data => data.forEach((item) => {
  item.input_quantity = item.quantity
})

// ...
const
transformedDataSource = dataSoruceAdapter(defaultDataSource); const [orderList, setOrderList] = useState(transformedDataSource);

後面發現, 陣列是空的... 囧

感覺自己真蠢, 改一下:

const dataSoruceAdapter = data => data.map((item) => {
  item.input_quantity = item.quantity
  return item
})

搞定。

我們仔細看一下forEach 和 map 這兩個方法:

對比和結論

forEach: 針對每一個元素執行提供的函式。

map: 建立一個新的陣列,其中每一個元素由呼叫陣列中的每一個元素執行提供的函式得來。

直接說結論吧:

forEach方法不會返回執行結果,而是undefined。

也就是說,forEach會修改原來的陣列,而map方法會得到一個新的陣列並返回。

下面我們看下具體的例子。

forEach

forEach 方法按升序為陣列中含有效值的每一項執行一次callback 函式,那些已刪除或者未初始化的項將被跳過(例如在稀疏陣列上)。

forEach 接收兩個引數: arr.forEach(callback[, thisArg]);

callback 函式會被依次傳入三個引數:

  1. 陣列當前項的值
  2. 陣列當前項的索引
  3. 陣列物件本身

比如:

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,3

如果 thisArg 引數有值,則每次 callback 函式被呼叫的時候,this 都會指向 thisArg 引數上的這個物件。

如果省略了 thisArg 引數, 或者賦值為 null 或 undefined,則 this 指向全域性物件。

舉個勉強的例子,從每個陣列中的元素值中更新一個物件的屬性:

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;
// 16 

如果使用箭頭函式來傳入函式引數,thisArg 引數會被忽略,因為箭頭函式在詞法上綁定了 this 值。

一般情況下, 我們只會用到callback的前兩個引數。

map

map 做的事情和 for迴圈 一樣,不同的是, map 會建立一個新陣列。

所以, 如果你不打算使用返回的新陣列, 卻依舊使用map的話, 這是違背map的設計初衷的。

map 函式接收兩個引數:

  1. callback
    callback 也有三個引數:

    1. currentValue
    2. index
    3. array
  2. thisArg(可選)

這一點和forEach 是類似的。

具體的例子:


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,3

callback 方法用例:

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

map 之後的結果是一個新陣列, 和原陣列是不相等的:

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

你也可以指定第二個引數thisArg的值:

const obj = { name: 'Jane' };

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

但是如果你使用的是箭頭函式, 此時的 this將會是 window:

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

箭頭函式和普通函式的工作機制是不同的,不是本範圍,你可以看這篇文章瞭解具體細節:https://www.geeksforgeeks.org...

寫到這裡, 就想起來一個經典的題目。

一個使用技巧案例

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

這道題我們都見過, 我們期望輸出 [1, 2, 3], 而實際結果是 [1, NaN, NaN]。

我們簡單來分析下過程, 首先看一下引數傳遞情況:

// parseInt(string, radix) -> map(parseInt(value, index))
第一次迭代(index is 0): parseInt("1", 0); // 1
第二次迭代(index is 1):  parseInt("2", 1); // NaN
第三次迭代(index is 2): parseInt("3", 2); //NaN

看到這, 解決辦法也就呼之欲出了:

function returnInt(element) {
  return parseInt(element, 10);
}

['1', '2', '3'].map(returnInt); // [1, 2, 3]

是不是很容易理解?

回到正題。

資源搜尋網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com

forEach 和 map 的區別

看兩行程式碼你就懂了:

[1,2,3].map(d => d + 1); // [2, 3, 4];
[1,2,3].forEach(d => d + 1); // undefined;

vue作者,尤雨溪大佬,有這麼一個形象的比方:

foreach 就是你按順序一個一個跟他們做點什麼,具體做什麼,隨便:

people.forEach(function (dude) {
  dude.pickUpSoap();
});

map 就是你手裡拿一個盒子(一個新的陣列),一個一個叫他們把錢包扔進去。結束的時候你獲得了一個新的陣列,裡面是大家的錢包,錢包的順序和人的順序一一對應。

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

reduce 就是你拿著錢包,一個一個數過去看裡面有多少錢啊?每檢查一個,你就和前面的總和加一起來。這樣結束的時候你就知道大家總共有多少錢了。

var totalMoney = wallets.reduce(function (countedMoney, wallet) {
   return countedMoney + wallet.money;
}, 0);

十分的貼切。