JavaScript 中的函數(shù)式編程原理

Doing some research, I found functional programming concepts like immutability and pure functions. Those concepts enable you to build side-effect-free functions, so it is easier to maintain systems -- with some other benefits.
(通過一些調(diào)查潜腻,我發(fā)現(xiàn)函數(shù)式編程的概念像是不可變以及純函數(shù)方法。這些概念能使你建立沒有副作用的函數(shù)社裆,因此很容易去管理系統(tǒng)制市,還有一些好處)

what is functional programming?

Function programming is a programming paradigm - a style of building the structure and elements of computer programs - that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data -- Wikipedia.

how do we know if a function is pure or not? Here is a very strict definition od purity(怎么去判斷一個函數(shù)是不是純函數(shù)):

  • It return the same result if given the same arguments(it is also referred as deterministic)(同樣的輸入得到同樣的輸出-確定性)
  • It does not cause any observable side effects(不會引起任何明顯的副作用)

It returns the same result if given the same arguments

let PI = 3.14;

const calculateArea = (radius) => radius * radius * PI;

calculateArea(10); // returns 314.0

Observation: mutability is discouraged in functional programming.(可變性在函數(shù)式編程中是不鼓勵的)

let counter = 1;

function increaseCounter(value) {
  counter = value + 1;
}

increaseCounter(counter);
console.log(counter); // 2

How would we make it pure?(怎么使他編程存函數(shù))

let counter = 1;

function increaseCounter(value) {
  counter = value + 1;
}

increaseCounter(counter);
console.log(counter); // 2

Pure functions are stable, consistent, and predictable. Given the same parameters, pure functions will always return the same result. We don't need to think of situations when the same parameter has different results - because it will never happen.(純函數(shù)是穩(wěn)定的,唯一的和可預(yù)測的渗磅,給與相同的參數(shù)犯眠,純函數(shù)將總是返回相同的結(jié)果按灶。我們不需要考慮當(dāng)參數(shù)相同,結(jié)果不相同的場景筐咧,因?yàn)檫@絕對不會發(fā)生)

pure functions benefits(純函數(shù)的好處)

The code's definitely easier to test.(該代碼絕對易于測試)

let list = [1, 2, 3, 4, 5];

const incrementNumbers = (list) => list.map(number => number + 1);

incrementNumbers(list); // [2, 3, 4, 5, 6]

Immutability

Unchanging over time or unable to be changed.

Wen data is immutable, its state cannot change after it's created. If you want to change an immutable object, you can't. instead, you create a new object with the new value.(當(dāng)數(shù)據(jù)是不可變的鸯旁,在創(chuàng)建的時(shí)候,他的狀態(tài)是不能變化量蕊,你絕不能改變一個不可變的對象铺罢,你可以用新值創(chuàng)建一個新對象)

var values = [1, 2, 3, 4, 5];
var sumOfValues = 0;

for (var i = 0; i < values.length; i++) {
  sumOfValues += values[I];
}

sumOfValues // 15

How do we handle mutability in iteration? Recursion.(我們?nèi)绾翁幚淼械目勺冃裕窟f歸残炮。)

let list = [1,2,3,4,5]
let accumulator = 0;

function sum(list, accumulator) {
  if(!list.length) {
    return accumulator;
  }
  return sum(list.splice(1), accumulator + list[0])
}

sum(list, accumulator); // 15
list; // [1, 2, 3, 4, 5]
accumulator; // 0

Observation: We can use reduce to implement this function.(我們能用 reduce 來實(shí)現(xiàn)這個函數(shù))

let list = [1,2,3,4,5]
let accumulator = 0;

function sum(list, accumulator) {
  return list.reduce((accumulator_Q, current) => {
    return accumulator_Q + current
  })
}

sum(list, accumulator); // 15
list; // [1, 2, 3, 4, 5]
accumulator; // 0
const string = " I will be a url slug   ";

const slugify = string =>
  string
    .toLowerCase()
    .trim()
    .split(" ")
    .join("-");

slugify(string); // i-will-be-a-url-slug

Referential transparency(參照透明)

const sum = (a, b) => a + b;

sum(3, sum(5, 8));

functions as first-class entities (函數(shù)是一等公民)

Function as first-class entities can:

  • refer to it from constants and variables(從常量和變量中引用它)
  • pass it as a parameter to other functions(作為參數(shù)傳遞給其他函數(shù))
  • return it as result from other functions(從其他函數(shù)作為結(jié)果返回)

This idea is treat functions as values and pass functions like data. This way we can combine different functions to create new functions with new behavior.(這個想法是將函數(shù)視為值韭赘,并將函數(shù)作為數(shù)據(jù)傳遞。這樣势就,我們可以組合不同的功能來創(chuàng)建具有新行為的新功能泉瞻。)

const sum = (a, b) => a + b;
const subtraction = (a, b) => a - b;

const doubleOperator = (f, a, b) => f(a, b) * 2;

doubleOperator(sum, 3, 1); // 8
doubleOperator(subtraction, 3, 1); // 4

filter

Syntax

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

Parameters
  • callback: Function is a predicate, to test each element of the array. Return true to keep the element, false otherwise. It accepts three arguments:
    • element: The current element being processed in the array.
    • index | Optional: The index of the current element being processed in the array.
    • array | Optional: The array filter was called upon.
  • thisArg | Optional: Value to use as this when executing callback.
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var evenNumbers = [];

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] % 2 == 0) {
    evenNumbers.push(numbers[i]);
  }
}

console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10]

const even = num => num % 2 === 0
const listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
listOfNumbers.filter(even)

var filterArray = function(x, coll) {
  var resultArray = [];

  for (var i = 0; i < coll.length; i++) {
    if (coll[i] < x) {
      resultArray.push(coll[i]);
    }
  }

  return resultArray;
}

console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0]

function smaller(number) {
  return number < this;
}

function filterArray(x, listOfNumbers) {
  return listOfNumbers.filter(smaller, x);
}

let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0];

filterArray(3, numbers);

map

The map method transforms a collection by applying a function to all of its elements and building a new collections from the returned values.(map方法通過將函數(shù)應(yīng)用于其所有元素并根據(jù)返回的值構(gòu)建新的集合來轉(zhuǎn)換集合。)

var people = [
  { name: "TK", age: 26 },
  { name: "Kaio", age: 10 },
  { name: "Kazumi", age: 30 }
];

var peopleSentences = [];

for (var i = 0; i < people.length; i++) {
  var sentence = people[i].name + " is " + people[i].age + " years old";
  peopleSentences.push(sentence);
}

console.log(peopleSentences); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']
const makeSentence = (person) => `${person.name} is ${person.age} years old`;

const peopleSentences = (people) => people.map(makeSentence);
  
peopleSentences(people);
// ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old']

var values = [1, 2, 3, -4, 5];

for (var i = 0; i < values.length; i++) {
  values[i] = Math.abs(values[i]);
}

console.log(values); // [1, 2, 3, 4, 5]
let values = [1, 2, 3, -4, 5];

const updateListMap = (values) => values.map(Math.abs);

updateListMap(values); // [1, 2, 3, 4, 5]

reduce

The idea of reduce is to receive a function and a collection, and return a value created by combining the items.(reduce的想法是接收一個函數(shù)和一個集合苞冯,并返回通過組合項(xiàng)目創(chuàng)建的值袖牙。)

Syntax

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

Parameters
  • callback: A function to execute on each element in the array (except for the first, if no initialValue is supplied), taking four arguments(在數(shù)組中的每個元素上執(zhí)行的函數(shù)(第一個元素除外,如果未提供initialValue的話)舅锄,帶有四個參數(shù):):
    • accumulator: The accumulator accumulates the callback's return values. It is the accumulated value previously returned in the last invocation of the callback, or initialValue, if supplied (see below).
    • currentValue: The current element being processed in the array.
    • index | Optional: The index of the current element being processed in the array. Starts from index 0 if an initialValue is provided. Otherwise, starts from index 1.
    • array | Optional: The array reduce() was called upon.
  • initialValue | Optional: A value to use as the first argument to the first call of the callback. If no initialValue is supplied, the first element in the array will be used and skipped. Calling reduce() on an empty array without an initialValue will throw a TypeError.
var orders = [
  { productTitle: "Product 1", amount: 10 },
  { productTitle: "Product 2", amount: 30 },
  { productTitle: "Product 3", amount: 20 },
  { productTitle: "Product 4", amount: 60 }
];

var totalAmount = 0;

for (var i = 0; i < orders.length; i++) {
  totalAmount += orders[i].amount;
}

console.log(totalAmount); // 120
let shoppingCart = [
  { productTitle: "Product 1", amount: 10 },
  { productTitle: "Product 2", amount: 30 },
  { productTitle: "Product 3", amount: 20 },
  { productTitle: "Product 4", amount: 60 }
];

const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount;

const getTotalAmount = (shoppingCart) => shoppingCart.reduce(sumAmount, 0);

getTotalAmount(shoppingCart); // 120

Another way to get the total amount is to compose map and reduce. What do I mean by that? We can use map to transform the shoppingCart into a collection of amount values, and then just use the reduce function with sumAmount function.(獲得總量的另一種方法是組合 map 并進(jìn)行 reduce鞭达。那是什么意思我們可以使用map 將 shoppingCart 轉(zhuǎn)換為金額值的集合,然后僅將 reduce 函數(shù)與 sumAmount 函數(shù)一起使用巧娱。)


const getAmount = (order) => order.amount;
const sumAmount = (acc, amount) => acc + amount;

function getTotalAmount(shoppingCart) {
  return shoppingCart
    .map(getAmount)
    .reduce(sumAmount, 0);
}

getTotalAmount(shoppingCart); // 120

參考文檔:
Functional Programming Principles in Javascript

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碉怔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子禁添,更是在濱河造成了極大的恐慌,老刑警劉巖桨踪,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件老翘,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)铺峭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門墓怀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人卫键,你說我怎么就攤上這事傀履。” “怎么了莉炉?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵钓账,是天一觀的道長。 經(jīng)常有香客問我絮宁,道長梆暮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任绍昂,我火速辦了婚禮啦粹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘窘游。我一直安慰自己唠椭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布忍饰。 她就那樣靜靜地躺著泪蔫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪喘批。 梳的紋絲不亂的頭發(fā)上撩荣,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機(jī)與錄音饶深,去河邊找鬼餐曹。 笑死,一個胖子當(dāng)著我的面吹牛敌厘,可吹牛的內(nèi)容都是我干的台猴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼俱两,長吁一口氣:“原來是場噩夢啊……” “哼饱狂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宪彩,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤休讳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后尿孔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俊柔,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筹麸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雏婶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片物赶。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖留晚,靈堂內(nèi)的尸體忽然破棺而出酵紫,到底是詐尸還是另有隱情,我是刑警寧澤错维,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布奖地,位于F島的核電站,受9級特大地震影響需五,放射性物質(zhì)發(fā)生泄漏鹉动。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一宏邮、第九天 我趴在偏房一處隱蔽的房頂上張望泽示。 院中可真熱鬧,春花似錦蜜氨、人聲如沸械筛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽埋哟。三九已至,卻和暖如春郎汪,著一層夾襖步出監(jiān)牢的瞬間赤赊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工煞赢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抛计,地道東北人。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓照筑,卻偏偏與公主長得像吹截,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凝危,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容