大部分前端都可能搞錯的基礎問題?(forEach會不會修改原陣列和sort排序)

語言: CN / TW / HK

forEach() 介紹

forEach()方法需要一個回撥函式(這種函式,是由我們建立但是不由我們呼叫的)作為引數

回撥函式中傳遞三個引數:

  • 第一個引數,就是當前正在遍歷的元素
  • 第二個引數,就是當前正在遍歷的元素的索引
  • 第三個引數,就是正在遍歷的陣列

程式碼舉例:

```js let myArr = ['王一', '王二', '王三'];

myArr.forEach((item, index, arr) => { console.log('item:' + item); console.log('index:' + index); console.log('arr:' + JSON.stringify(arr)); }); ```

列印結果:

```js item:王一 index:0 arr:["王一","王二","王三"]


item:王二 index:1 arr:["王一","王二","王三"]


item:王三 index:2 arr:["王一","王二","王三"]


```

注意:forEach() 沒有返回值。也可以理解成:forEach() 的返回值是 undefined

let tempArry = myArr.forEach() 這種方式接收是沒有意義的

forEach() 能不能改變原陣列?

forEach() 能不能改變原陣列?關於這個問題,大部分人會搞錯。我們來看看下面的程式碼

1、陣列的元素是基本資料型別:(無法改變原陣列)

```js let numArr = [1, 2, 3];

numArr.forEach((item) => { item = item * 2; }); console.log(numArr); // 列印結果:[1, 2, 3] ```

上面這段程式碼,你可要看仔細了,列印結果是 [1, 2, 3],不是 [2, 4, 6]

2、陣列的元素是引用資料型別:(直接修改整個元素物件時,無法改變原陣列)

```js let objArr = [ { name: '雲牧', age: 20 }, { name: '許嵩', age: 30 }, ];

objArr.forEach((item) => { item = { name: '鄧紫棋', age: '29', }; }); console.log(JSON.stringify(objArr)); // 列印結果:[{"name": "雲牧","age": 20},{"name": "許嵩","age": 30}] ```

3、陣列的元素是引用資料型別:(修改元素物件裡的某個屬性時,可以改變原陣列)

```js let objArr = [ { name: '雲牧', age: 28 }, { name: '許嵩', age: 30 }, ];

objArr.forEach((item) => { item.name = '鄧紫棋'; }); console.log(JSON.stringify(objArr)); // 列印結果:[{"name":"鄧紫棋","age":28},{"name":"鄧紫棋","age":30}] ```

如果你需要通過 forEach 修改原陣列,建議用 forEach 裡面的引數 2 和引數 3 來做,具體請看下面的標準做法

forEach() 通過引數 2、引數 3 修改原陣列:(標準做法)

```js // 1、陣列的元素是基本資料型別 let numArr = [1, 2, 3];

numArr.forEach((item, index, arr) => { arr[index] = arr[index] * 2; }); console.log(JSON.stringify(numArr)); // 列印結果:[2, 4, 6]

// 2、陣列的元素是引用資料型別時,直接修改物件 let objArr = [ { name: '雲牧', age: 28 }, { name: '許嵩', age: 34 }, ];

objArr.forEach((item, index, arr) => { arr[index] = { name: '小明', age: '10', }; }); console.log(JSON.stringify(objArr)); // 列印結果:[{"name":"小明","age":"10"},{"name":"小明","age":"10"}]

// 3、陣列的元素是引用資料型別時,修改物件的某個屬性 let objArr2 = [ { name: '雲牧', age: 28 }, { name: '許嵩', age: 34 }, ];

objArr2.forEach((item, index, arr) => { arr[index].name = '小明'; }); console.log(JSON.stringify(objArr2)); // 列印結果:[{"name":"小明","age":28},{"name":"小明","age":34}] ```

總結

如果純粹只是遍歷陣列,那麼,可以用 forEach() 方法

但是,如果你想在遍歷陣列的同時,去改變數組裡的元素內容,那麼,最好是用 map() 方法來做,不要用 forEach()方法,避免出現一些低階錯誤

sort() 介紹

sort():對陣列的元素進行從小到大來排序(會改變原來的陣列)

如果在使用 sort() 方法時不帶參,

預設排序順序是在將元素轉換為字串按照Unicode 編碼,從小到大進行排序

舉例 1:(當陣列中的元素為字串時)

```js let arr1 = ['e', 'b', 'd', 'a', 'f', 'c'];

let result = arr1.sort(); // 將陣列 arr1 進行排序

console.log('arr1 =' + JSON.stringify(arr1)); console.log('result =' + JSON.stringify(result)); ```

列印結果:

js arr1 =["a","b","c","d","e","f"] result =["a","b","c","d","e","f"]

從上方的列印結果中,我們可以看到,sort 方法會改變原陣列,而且方法的返回值也是同樣的結果

舉例 2:(當陣列中的元素為數字時)

```js let arr2 = [5, 2, 11, 3, 4, 1];

let result = arr2.sort(); // 將陣列 arr2 進行排序

console.log('arr2 =' + JSON.stringify(arr2)); console.log('result =' + JSON.stringify(result)); ```

列印結果:

js arr2 = [1,11,2,3,4,5] result = [1,11,2,3,4,5]

上方的列印結果中,你會發現,使用 sort() 排序後,數字11竟然在數字2的前面。這是為啥呢?因為上面講到了,sort()方法是按照Unicode 編碼進行排序的。

那如果我想讓 arr2 裡的數字,完全按照從小到大排序,怎麼操作呢?繼續往下看。

sort()方法:帶參時,自定義排序規則

如果在 sort()方法中帶參,我們就可以自定義排序規則。具體做法如下:

我們可以在 sort()新增一個回撥函式,來指定排序規則。

回撥函式中需要定義兩個形參,瀏覽器將會分別使用陣列中的元素作為實參去呼叫回撥函式。

瀏覽器根據回撥函式的返回值來決定元素的排序:(重要)

  • 如果 compareFunction(a, b) 小於 0 ,那麼 a 會被排列到 b 之前;
  • 如果 compareFunction(a, b) 等於 0 , a 和 b 的相對位置不變
  • 如果 compareFunction(a, b) 大於 0 , b 會被排列到 a 之前

如果只是看上面的文字,可能不太好理解,我們來看看下面的例子,你肯定就能明白

js let arr = [5, 2, 11, 3, 4, 1]; arr.sort(function (a, b) { console.log("a:" + a, "b:" + b); }); /* a:2 b:5 a:11 b:2 a:3 b:11 a:4 b:3 a:1 b:4 */

舉例:將陣列中的數字按照從小到大排序

寫法 1:

```js let arr = [5, 2, 11, 3, 4, 1];

// 自定義排序規則 let result = arr.sort(function (a, b) { if (a > b) { // 如果 a 大於 b,則 b 排列 a 之前 return 1; } else if (a < b) { // 如果 a 小於 b,,則 a 排列 b 之前 return -1; } else { // 如果 a 等於 b,則位置不變 return 0; } });

console.log('arr =' + JSON.stringify(arr)); console.log('result =' + JSON.stringify(result)); ```

列印結果:

js arr = [1, 2, 3, 4, 5, 11]; result = [1, 2, 3, 4, 5, 11];

上方程式碼的寫法太囉嗦了,其實也可以簡化為如下寫法:

寫法 2:

```js let arr = [5, 2, 11, 3, 4, 1];

// 自定義排序規則 let result = arr.sort(function (a, b) { return a - b; // 升序排列 // return b - a; // 降序排列 });

console.log('arr =' + JSON.stringify(arr)); console.log('result =' + JSON.stringify(result)); ```

列印結果不變。

上方程式碼還可以寫成 ES6 的形式,也就是將 function 改為箭頭函式,其寫法如下

寫法 3:(箭頭函式)

```js let arr = [5, 2, 11, 3, 4, 1];

// 自定義排序規則 let result = arr.sort((a, b) => { return a - b; // 升序排列 });

console.log('arr =' + JSON.stringify(arr)); console.log('result =' + JSON.stringify(result)); ```

上方程式碼,因為函式體內只有一句話,所以可以去掉 return 語句,繼續簡化為如下寫法

寫法 4:(推薦)

```js let arr = [5, 2, 11, 3, 4, 1];

// 自定義排序規則:升序排列 let result = arr.sort((a, b) => a - b);

console.log('arr =' + JSON.stringify(arr)); console.log('result =' + JSON.stringify(result)); ```

上面的各種寫法中,寫法 4 是我們在實戰開發中用得最多的。

為了確保程式碼的簡潔優雅,接下來的程式碼中,凡是涉及到函式,我們將盡量採用 ES6 中的箭頭函式來寫

sort 方法舉例:將陣列按裡面某個欄位從小到大排序

將陣列從小到大排序,這個例子很常見。但在實際開發中,總會有一些花樣。

下面這段程式碼,在實際開發中,經常用到,一定要掌握。完整程式碼如下:

```js let dataList = [ { title: "品牌鞋子,高品質低價入手", publishTime: 200, }, { title: "不是很貴,但是很暖", publishTime: 100, }, { title: "無法拒絕的美食,跟我一起吃吃", publishTime: 300, }, ];

console.log("qianguyihao 排序前的陣列:" + JSON.stringify(dataList));

  // 將dataList 陣列,按照 publishTime 欄位,從小到大排序。(會改變原陣列)
  dataList.sort((a, b) => parseInt(a.publishTime) -                                           parseInt(b.publishTime));

console.log("qianguyihao 排序後的陣列:" + JSON.stringify(dataList)); ```

列印結果:

```js qianguyihao 排序前的陣列:[ {"title":"品牌鞋子,高品質低價入手","publishTime":200}, {"title":"不是很貴,但是很暖","publishTime":100}, {"title":"無法拒絕的美食,跟我一起吃吃","publishTime":300} ]

qianguyihao 排序後的陣列:[ {"title":"不是很貴,但是很暖","publishTime":100}, {"title":"品牌鞋子,高品質低價入手","publishTime":200}, {"title":"無法拒絕的美食,跟我一起吃吃","publishTime":300} ] ```

上方程式碼中,有人可能會問: publishTime 欄位已經是 Number 型別了,為啥在排序前還要做一次 parseInt() 轉換?

這是因為,這種資料,一般是後臺介面返回給前端的,資料可能是 Number 型別、也可能是字串型別,所以還是統一先做一次 partInt() 比較保險。這是一種良好的工作習慣