0%

[JS] 講座筆記:函數式程式設計

這篇筆記來自於 2020.1.9 六角學院的線上研討會,講者為蘇泰安老師,老師不只講解了如何善用函數來幫助程式碼更簡潔,也介紹了一些以函數為編寫基礎的語言(例如 Elixir),不過由於我對 Javascript 以外的語言並不了解,所以這篇筆記並不會著墨於其他語言。

前言

在 JavaScript 函式是一級公民,可以傳入另一個函式作為參數。
傳入其他函式作為參數的函式稱為 Callback function。
而接收其他函式當作自己的參數的則稱為高階函式。

高階函式(High order function):處理函式的函式

1
2
3
4
5
6
7
function cook (food1,food2, f) {
f(food1);
f(food2);
console.log(`${food1} in ${food2}`);
}

cook('beef', 'curry', function(x) {console.log(x)});

高階函式三大台柱:map()reduce()filter()

map()

對 A 陣列進行某種處理,回傳處理過後的新陣列 B。

1
2
3
let result = [1,2,3,4,5].map(i => i * 10);
console.log(result);
// [10,20,30,40,50]

filter()

從 A 陣列中篩選符合某條件的值,符合條件的值以新陣列 B 回傳。

1
2
3
let result = [1,2,3,4,5,6].map(i => i % 2 == 0);
console.log(result);
// [2,4,6]

reduce()

有順序上的依賴,依序對陣列內的值進行處理(累加、相乘等等)。

1
2
3
4
5
6
// for 迴圈的累加寫法
let ary = [1,2,3,4,5,6];
let result = 0;
for (let i of ary){
result = result + i;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// reduce() 的累加寫法
let result = [1,2,3,4,5,6].reduce(reducer, 0);
function reducer(accu, i){
return accu + i;
}

// 起始值為 0
// accu 為一個暫存、累加的值,就如同上一段在 for 迴圈之前宣告的 result
// 第一次遞迴:accu 為 0,i 為 1,相加為 1
// 第二次遞迴:accu 為 1,i 為 2,相加為 3
// 以下類推

// 起始值的型別跟最後運算完的型別一致
// 常用的起始值:0, 1, [], '', {}

迴圈跟 map()reduce()filter() 的差別:

迴圈一定要依序對值進行處理,就像手搖店員對一個接一個的客人進行結帳;陣列方法不強調值的處理順序,只強調處理的手法。

閉包 Closure

利用閉包(closure)的作法,讓函式有自己的私有變數,避免函式受到外在值的變動而被污染。當你看到一個函式內 return 了另一個函式,通常就是有用到閉包的概念。
參照:Robert Nyman: 解釋 JavaScript 的 scope 及 closures深入淺出瞭解 JavaScript 閉包(closure)

1
2
3
4
5
6
7
8
9
10
// 以下情形會讓函式的結果受到外在變數的污染
let a = 1;
let foo = function(i) { return a + i}
console.log(foo(100));
a = 99;
console.log(foo(100));

// 兩次 console 結果不一樣
// 因為函式會吃到參數以外的變數值
// 而且改變變數值時,也會影響到函式結果
1
2
3
4
5
6
7
8
9
10
11
function fooDad() {
let a = 1;
function foo(i) {
return a + i;
};
return foo;
};
const foo = fooDad();
console.log(foo(100)); // 101
a = 99;
console.log(foo(100)); // 101,不受污染

學員提問

Q: 具名函式 VS 匿名函式的區別?
A: 具名函式可提升,匿名不能提升(會 undefined)。