上次我們提到如何新增 instance method,讓我們先來複習一下

添加 instance method

JavaScript 是 prototype based 的程式語言,比起其他程式語言特別的地方是,他們具有 function 這個型別
我們會稱呼他為原型物件,但是我們還是會直接稱它為物件

copyright by Giamir

每個原型物件也會有自己的原型而形成原型鏈,最後直到接到 null 為止

copyright by pjchender

當我們要在特定的原型添加屬性或是函數的話,只需要直接呼叫 prototype 來實作即可:

1
2
3
4
5
6
7
8
9
function Example() {
// ...
}

Example.prototype.value = 123;

Example.prototype.myFunction = function () {
console.log("hey myFunction");
};

如此一來,基於原型實現的物件就可以擁有這些原型方法可以用了:

1
2
3
4
const a = new Example();

console.log(a.value);
a.myFunction();

實作 forEach

讓我們先來看看 forEach 是如何使用

1
2
3
4
5
const array = [1, 2, 3];

array.forEach((value) => {
console.log(value);
});

關於 Currying

copyright by Ranando King

把接受多個參數的 function 變成只接受單一一個參數的 function,我們就稱它為 Currying function。
對一開始接觸他的人,就像是在天書一樣,甚至不知道為什麼要這樣設計。
在討論 Currying 之前,讓我們先來看看他究竟解決了什麼問題吧

在數學中,把(A, B, C) -> D 變成 A -> B -> C -> D
他所做的事情就是把 function 的接口單純化
而 JavaScript 又剛好可以使用閉包搭配遞迴的操作來做到這樣 functional programming 的操作
所以也就被一些人拿來當作簡潔程式碼的攥寫方式了

例如我要實現 add 要加總:

1
2
3
4
5
const add = (a, b, c) => {
return a + b + c;
};

add(1, 2, 3);

我今天希望把它從(A, B, C) -> D 變成 A -> B -> C -> D

那我就可以這樣做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const add = (a) => {
console.log("a: ", a);
return (b) => {
return (c) => {
return a + b + c;
};
};
};

add(1)(2)(3);

const add1 = add(1);

// 兩種寫法
const add3 = add(1)(2);
const add3 = add1(2);

// 三種寫法
const add6 = add(1)(2)(3);
const add6 = add1(2)(3);
const add6 = add3(3);

這個時候你就會發現,他擁有遞迴的概念,只是遞迴的對象變成了 function
所以我們可以這樣寫:

1
2
3
4
5
6
7
8
9
10
11
12
const add = (num) => {
return (nextNum) => {
if (nextNum) {
return add(num + nextNum);
} else {
return num;
}
};
};

const test = add();
console.log(test(1, 2, 3));

根據那神奇的數學公式呢,我們可以可以把它變成以下的 function:

1
2
3
4
5
6
const curry =
(func) =>
(...args) =>
args.length < func.length
? (...moreArgs) => curry(func)(...args, ...moreArgs)
: func(...args);

看起來很可怕,不過我們來 step by step

我們知道根據公式,currying 是只有一個參數
所以可以先寫成這樣: A -> (…)

1
2
3
const curry = (func) => {
// ...
};

再來我們把剩下那一大坨參數丟到裡面的 function
記住喔,因為是串聯起來的,所以直接 return 它

1
2
3
4
5
6
const curry = (func) => {
const innerCurrying = (...args) => {
// ...
};
return innerCurrying;
};

再來我們要判斷剩下的那一坨參數的數量為多少

1
2
3
4
5
6
7
8
9
10
const curry = (func) => {
const innerCurrying = (...args) => {
if (args.length > func.length) {
return (...moreArgs) => curry(func)(...args, ...moreArgs);
} else {
return func(...args);
}
};
return innerCurrying;
};

Currying 實際應用

今天我希望使用 map 讓 array 所有的值都 double,可以這樣設計:

1
2
3
4
5
6
7
const array = [1, 2, 3, 4, 5];

const doubleNumber = (num) => num * 2;

const result = array.map(doubleNumber);

console.log("result: ", result);

但是之後突然多了其他需求:

1
2
3
4
const array = [1, 2, 3, 4, 5];

const doubleNumber = (num) => num * 2;
const tripleNumber = (num) => num * 3;

哎呀,多了一個結構類似的 method 了,我們來簡化他吧

1
2
3
4
5
6
7
const array = [1, 2, 3, 4, 5];

const doubleNumber = (num) => num * 2;

const result = (n) => array.map(doubleNumber);

console.log("result: ", result(2));

Comment