JavaScript 中的函数式编程原理

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.
(通过一些调查,我发现函数式编程的概念像是不可变以及纯函数方法。这些概念能使你建立没有副作用的函数,因此很容易去管理系统,还有一些好处)

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(怎么去判断一个函数是不是纯函数):

  • 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.(可变性在函数式编程中是不鼓励的)

let counter = 1;

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

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

How would we make it pure?(怎么使他编程存函数)

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.(纯函数是稳定的,唯一的和可预测的,给与相同的参数,纯函数将总是返回相同的结果。我们不需要考虑当参数相同,结果不相同的场景,因为这绝对不会发生)

pure functions benefits(纯函数的好处)

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.(当数据是不可变的,在创建的时候,他的状态是不能变化,你绝不能改变一个不可变的对象,你可以用新值创建一个新对象)

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.(我们如何处理迭代中的可变性?递归。)

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 来实现这个函数)

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 (函数是一等公民)

Function as first-class entities can:

  • refer to it from constants and variables(从常量和变量中引用它)
  • pass it as a parameter to other functions(作为参数传递给其他函数)
  • return it as result from other functions(从其他函数作为结果返回)

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.(这个想法是将函数视为值,并将函数作为数据传递。这样,我们可以组合不同的功能来创建具有新行为的新功能。)

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方法通过将函数应用于其所有元素并根据返回的值构建新的集合来转换集合。)

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的想法是接收一个函数和一个集合,并返回通过组合项目创建的值。)

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(在数组中的每个元素上执行的函数(第一个元素除外,如果未提供initialValue的话),带有四个参数:):
    • 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 并进行 reduce。那是什么意思我们可以使用map 将 shoppingCart 转换为金额值的集合,然后仅将 reduce 函数与 sumAmount 函数一起使用。)


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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容