別為了你現在正想要的,而放棄你最想要的。
Richard G. Scott
本篇不是討論Partial Applications本身,而是他在控制流程中的優勢!
今天我們將介紹最後一個關於控制流程的議題。
如果你發現你正在用一大堆(真的是一大堆)的陳述句或是運算子來改變控制流程。這表示你的代碼編寫的太複雜了!很容易變成spaghetti代碼!
我相信你一定看過以下這種代碼,我這邊用偽代碼表示:
if (某個情況) {
const 要被更新 = [];
for (let i = 0; i < 某陣列的長度; i++) {
const 被改變的東東 = 改變它吧(某陣列[i]);
if (!被改變的東東) {
要被更新.push(某陣列[i]);
}
}
if (!要被更新的長度) {
return;
}
}
else {
const 這個服務 = 某個服務();
這個服務.做一些事();
if (這個服務.某個東西 != null) {
這個服務.做另一些事();
}
}
這是多麽雜亂的代碼啊!這不僅違反了許多前幾天我們談控制流程的原則,更重要的是if
與else
中所做的兩件事情毫無關係。如此一來,我們很難follow這樣的代碼邏輯,對於debug來說也是硬傷。
面對這種情況,我們的解決辦法是:記得這幾天的原則仔細切分邏輯(讓每個邏輯儘量平行),建構對應的函式,並賦予其有意義的名字。
有時候,你所建構的函式內部會包含了一些條件式,此時就非常適合考慮partial applications(或是currying functions)。
由於partial applications本身就仰賴closure。在了解partial applications前,我們先來簡單回顧一下什麼是closure。
閉包closure,簡言之就是一個函式(外函式),最後返回另一個函式(內函式),當內函式被invoke時,內函式是可以access外函式的變數的。也就是函式有自己私有變數,即變數保留在該函式中而不會被外在環境干擾。
我們直接來看一個例子,讓回憶湧上心頭。
function person(name) {
let times = 0;
function voicePerson() {
times += 1;
console.log(`${name} voices ${times} times`);
}
return voicePerson;
}
const voicePeter = person('Peter');
const voiceMira = person('Mira');
voiceMira();
// Mira voices 1 times
voiceMira();
// Mira voices 2 times
voicePeter();
// Peter voices 1 times
voicePeter();
// Peter voices 2 times
voiceMira();
// Mira voices 3 times
此時函式中person
中的times
便是可以視為私有變數。事實上,上述這個例子,可以使用匿名函數,更為簡潔。我們在React中也會看到不少這樣的例子。
function person(name) {
let times = 0;
return () => {
times++;
console.log(`${name} voices ${times} times`);
}
}
const voicePeter = person('Peter');
const voiceMira = person('Mira');
voiceMira();
// Mira voices 1 times
voiceMira();
// Mira voices 2 times
voicePeter();
// Peter voices 1 times
voicePeter();
// Peter voices 2 times
voiceMira();
// Mira voices 3 times
有了closure的先備知識,明天我們就來看看它如何成為partial applications,並用來抽離簡化我們具有多重條件的邏輯。