Web開發學習筆記09 — 陣列操作方法(Array Methods)& 箭頭函式(Array Function)


Posted by Teagan Hsu on 2020-12-08

1. forEach

語法

arr.forEach(function callback(currentValue[, index[, array]]) {
    //your iterator
}[, thisArg]);

範例一

用forEach跑迴圈, 把陣列中每個元素執行一次。

let arr = [100,200,300,400];
arr.forEach(function(value){console.log(value);});

//100,200,300,400

範例二

let arr = [100,200,300,400];
let sum = 0;
arr.forEach(function(value){
    sum += value;
});
sum;

//1000

也可以使用更簡潔的寫法:ES6 arrow function

let arr = [100,200,300,400];
let sum = 0;
arr.forEach(value => sum+=value);
sum;

//1000

範例三

跑數列中的物件

let arr = [
    { fruit: 'apple', price: 10 },
    { fruit: 'orange', price: 20 },
    { fruit: 'banana', price: 5 }
];
arr.forEach(value => console.log(`${value.fruit} is $${value.price}`));

//apple is $10
//orange is $20
//banana is $5

範例四

this指向

let arr1 = [1,2,3];
let arr2 = [100,200,300,400];
arr1.forEach(function(value){console.log(this);}, arr2);

//[100, 200, 300, 400]
//[100, 200, 300, 400]
//[100, 200, 300, 400]
//最後跑出3個arr2

2. map

語法

let new_array = arr.map(function callback( currentValue[, index[, array]]) {
    // return element for new_array
}[, thisArg])

範例一

讓newArr儲存函式處理過的新陣列。

let arr = [2,4,6,8,10];
let newArr = arr.map(value => value*2);
newArr;

//[4, 8, 12, 16, 20]

範例二

讓newArr儲存陣列中的物件元素成為一個新陣列。

let arr = [
    { fruit: 'apple', price: 10 },
    { fruit: 'orange', price: 20 },
    { fruit: 'banana', price: 5 }
];
let newArr = arr.map(value => value.fruit);
newArr;

//["apple", "orange", "banana"]

forEach與map的差異

定義

forEach

接收函數作為參數,對每個陣列元素執行一次。但是forEach不會返回一個新陣列,而是返回undefined。

map

接收一個函數作為參數,然後將其應用於每個元素,並回傳一個新陣列,而且保持相同數量的元素。


來看看以下的不同,因爲map會回傳一個新的陣列,所以返回結果為[2,4,6,8,10],而forEach則是undefined

let arr = [1, 2, 3, 4, 5];
arr.forEach(value => value * 2);
arr.map(value => value * 2);

//undefined
//[2,4,6,8,10]s

再來是mapforEach更有串接性,意思就是map後面可以接更多methods

let arr = [1, 2, 3, 4, 5];
arr.forEach(value => value * 2).filter(result => result >= 8);
arr.map(value => value * 2).filter(result => result >= 8);

//Uncaught TypeError: Cannot read property 'filter' of undefined
//[8, 10]

3. Every

Every用來檢驗陣列中是否都有符合條件,如果全部都符合則回傳true。

語法

arr.every(callback[, thisArg])

範例一

let arr = [2, 4, 6];
arr.every((x) => { return x % 2 === 0 });

//true

4. Some

Some用來檢驗陣列中是否有任何一項是符合條件的,只要有一項符合則回傳true。

語法

arr.some(callback[, thisArg])

範例一

let arr = [1, 3, 6];
arr.some((x) => { return x % 2 === 0 });

//true

every與some的差異

every與some的差異在於,前者是要全部符合條件才會回傳true,後者則是只要符合一項就會回傳true,來看個比較的例子:

let arr = [1, 4, 6];
arr.every((x) => { return x % 2 === 0 });    //false
arr.some((x) => { return x % 2 === 0 });     //true

5. filter

filter顧名思義就是過濾,可以幫助我們篩選陣列的元素有沒有符合條件。如果元素符合條件則會傳出,組合成一個新的陣列。

語法

let newArray = arr.filter(callback(currentValue[, index[, array]]) {
  // return element for newArray, if true
}[, thisArg]);

範例一

篩選陣列大於50的元素:

let arr = [11, 20, 93, 70, 39, 84];
let newArray = arr.filter((x) => { return x >= 50; });
newArray;
// [93, 70, 84]

5. reduce

reduce會將陣列中的每一個元素傳入指定的函數,最後回傳一個累加值(accumulator)。簡單來說就是「累加器」的功能,但其實此方法非常強大,可以做更多運用(範例會介紹一些)。

語法

  1. accumulator:簡單而言就是目前累加的結果,初始預設值是initialValue
  2. currentValue:表示目前迭代處理的元素值。如果有傳入initialValue,則由索3. 引 0之元素開始,若無則自索引1之元素開始。
  3. currentIndex:表示目前迭代處理的元素之索引。
  4. array:表示陣列本身
  5. initialValue:表示初始的累加值
  6. accumulatorcurrentValue為必填,其餘為optional。
arr.reduce(callback[accumulator, currentValue, currentIndex, array], initialValue);

範例一:累加每項值

將陣列每項元素累加起來:

let arr = [1, 2, 3, 4];
let sum = arr.reduce((accumulator, currentValue) => { return accumulator + currentValue; });
sum;

//10;

範例二:物件陣列中的總和

想知道這三部電影的分數總和為多少?
要總和物件陣列中包含的值,必須提供initialValue,以便每項元素都通過函數。

let allMovie = [{
        title: 'The Godfather',
        score: 92,
        year: 1972
    }, {
        title: 'Joker',
        score: 85,
        year: 2019
    },
    {
        title: 'Forrest Gump',
        score: 88,
        year: 1994
    }
]
let sum = allMovie.reduce((acc, current) => { return acc + current.score }, 0);
sum;

//265

範例三:移除重複的元素

注意要加initialValue,否則會出現錯誤。

let arr = ['a', 'b', 'b', 'c', 'a', 'd', 'd', 'e', 'a', 'b'];
let removeDuplicateItems = arr.reduce((acc, current) => {
    if (acc.indexOf(current) === -1) {
        acc.push(current);
    }
    return acc;
}, []);
removeDuplicateItems;

//["a", "b", "c", "d", "e"]

範例四:代替map和filter功能

篩選正數,並讓結果回傳數值的平方:

let arr = [10, 8, -6, -12, 3];
let newArr = arr.reduce((acc, current) => {
    if (current > 0) {
        let double = Math.pow(current, 2);
        acc.push(double);
    }
    return acc;
}, []);
newArr;

//[100, 64, 9]

改用map和filter也可以達到一樣結果:

let arr = [10, 8, -6, -12, 3];
let newArr = arr.filter((x) => { return x > 0 }).map((y) => { return y * y });
newArr;

//[100, 64, 9]

範例五:找出陣列中的最大值

let arr = [10, 8, -6, -12, 3];
let newArr = arr.reduce((max, item) => {
    if (item > max) { return item; }
    return max;
})

範例六:找出物件陣列中的最小值

找出電影清單中分數最低的電影名稱:

let allMovie = [{
        title: 'The Godfather',
        score: 92,
        year: 1972
    }, {
        title: 'Joker',
        score: 85,
        year: 2019
    },
    {
        title: 'Forrest Gump',
        score: 88,
        year: 1994
    }
]

let lowestRatedMovies = allMovie.reduce((lowScoreMovie, currentMovie) => {
    if (currentMovie.score < lowScoreMovie.score) {
        return currentMovie;
    }
    return lowScoreMovie;
});

lowestRatedMovies.title;
//Joker

箭頭函式(Arrow Function)

const arr = [
    { fruit: 'apple', price: 10 },
    { fruit: 'orange', price: 20 },
    { fruit: 'banana', price: 5 }
];

//傳統寫法
let fruitPrice = arr.map(function(value) {
        return `${value.fruit} is $${value.price}`
    })

    //箭頭函式
let fruitPrice = arr.map((value) => {
        return `${value.fruit} is $${value.price}`
    })

    //箭頭函式,省略參數的括弧
let fruitPrice = arr.map(value => {
        return `${value.fruit} is $${value.price}`
    })

    //箭頭函式,省略參數的括弧,將{}改為()之後,可以省略return
let fruitPrice = arr.map(value => (
        `${value.fruit} is $${value.price}`))

    //箭頭函式,省略參數的括弧,省略函式的(),讓程式寫成一線。
let fruitPrice = arr.map(value => `${value.fruit} is $${value.price}`)

fruitPrice;
//["apple is $10", "orange is $20", "banana is $5"]

補充:

  • 如果參數無內容時,一定要加括弧()。例如以下範例:
let randomNum = () => (
Math.floor(Math.random()*10)+1);

randomNum()
//隨機出現1-10
  • 只有一個參數時,可以省略參數的括弧。
  • 函數中只需要回傳一個表達式時,將大括弧{}改為括弧()可以省略return
  • 函數中只需要回傳一個表達式時,也可乾脆省略函式的括弧(),這是最簡化。
  • 如果使用大括弧{}一定要加return,否則會出現undefined。以下範例:
const funcA = x => x * 2
const funcB = x => ( x * 2 )
const funcC = x => { x * 2 }
funcA(1);
//2

funcB(1);
//2

funcC(1);
//undefined

不可使用箭頭函式的情況

1.使用實字物件時(Object literal),讀取不到this.array的值

const calculate = {
 const calculate = {
    array: [1, 2, 3],
    select: () => {
        return this.array.filter((result) => result > 2)
    }
}
calculate.select();

  //Uncaught TypeError: Cannot read property 'filter' of undefined
    at Object.sum

改用傳統function,則可以正常跑出:

  const calculate = {
    array: [1, 2, 3],
    select: function(){
        return this.array.filter((result) => result>2)
    }
}
calculate.select();

//[3]

2.箭頭函式沒有constructorprototype
箭頭函式沒有constructorprototype,所以會顯示is not a constructor

const myFunction = (() => {
    this.value = 100
})
const result = new myFunction()
console.log(result.value)

//Uncaught TypeError: myFunction is not a constructor

改用傳統function,則可以正常跑出:

function myFunc() {
    this.value = 100
}
const result = new myFunc()
console.log(result.value)

//100

3.箭頭函式中的bind、call、apply無法修改原本的this值。

let arr = [1, 2, 3, 4, 5];
const myFunc = () => {
    return console.log(this)
};

myFunc.call(arr);
myFunc.apply(arr);
const result = myFunc.bind(arr);
result();

//this皆指向window與global object

改用傳統function,則可以正常跑出:

let arr = [1, 2, 3, 4, 5];
function myFunc(){
    return console.log(this)
};

myFunc.call(arr);
myFunc.apply(arr);
const result = myFunc.bind(arr);
result();

//[1, 2, 3, 4, 5]
//[1, 2, 3, 4, 5]
//[1, 2, 3, 4, 5]

4.在DOM事件監聽中,this會無法使用。
詳細示範可參照codepen

const FuncArrow = document.getElementById('btn-arrow')
FuncArrow.addEventListener('click', () => {
    this.innerHTML = 'Clicked the function'
})

//Uncaught TypeError: Cannot read property 'addEventListener' of null

改用function就可順利監聽事件:

const Func = document.getElementById('btn');
Func.addEventListener('click', function() {
    this.innerHTML = 'Clicked the function'
});

//按鈕的字:click變成Clicked the function

參考資料:
Array.prototype.reduce() - MDN
The Differences Between forEach() and map() that Every Developer Should Know
箭頭函式 - 從ES6開始的JavaScript學習生活Understanding Arrow Functions in JavaScript
鐵人賽:箭頭函式 (Arrow functions)


#Arrow Function #Arrow Methods #陣列方法 #箭頭函式







Related Posts

TCP/IP 四層模型/ HTTP 相關筆記

TCP/IP 四層模型/ HTTP 相關筆記

[cs50x - 2022] week3 演算法

[cs50x - 2022] week3 演算法

利用 JavaScript 實作簡易 TodoList

利用 JavaScript 實作簡易 TodoList


Comments