# JS中函数式编程基本原理简介

## 纯函数

• 如果给定相同的参数，则返回相同的结果(也称为确定性)。
• 它不会引起任何副作用。

## 如果给定相同的参数，则得到相同的结果

``````let PI = 3.14;

calculateArea(10); // returns 314.0
``````

``````let PI = 3.14;

calculateArea(10, PI); // returns 314.0
``````

• 对于参数`radius = 10``PI = 3.14`，始终都会得到相同的结果：`314.0`
• 对于 `radius = 10``PI = 42`，总是得到相同的结果:`4200`

## 读取文件

``````const charactersCounter = (text) => `Character count: \${text.length}`;

function analyzeFile(filename) {
let fileContent = open(filename);
return charactersCounter(fileContent);
}
``````

## 随机数生成

``````function yearEndEvaluation() {
if (Math.random() > 0.5) {
return "You get a raise!";
} else {
return "Better luck next year!";
}
}
``````

## 无明显副作用

``````let counter = 1;

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

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

``````let counter = 1;

const increaseCounter = (value) => value + 1;

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

``````

## 纯函数的好处

• 给定一个参数 `A`，期望函数返回值 `B`
• 给定一个参数`C`，期望函数返回值`D`

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

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

``````

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

## 不可变性

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

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

sumOfValues // 15
``````

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

function sum(list, accumulator) {
if (list.length == 0) {
return accumulator;
}

return sum(list.slice(1), accumulator + list[0]);
}

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

``````class UrlSlugify

def initialize(text)
@text = text
end

def slugify!
text.downcase!
text.strip!
text.gsub!(' ', '-')
end
end

UrlSlugify.new(' I will be a url slug   ').slugify! # "i-will-be-a-url-slug"
``````

``````const string = " I will be a url slug   ";

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

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

• `toLowerCase`:将字符串转换为所有小写字母。
• trim:删除字符串两端的空白。
• `split``join`：用给定字符串中的替换替换所有匹配实例

## 引用透明性

``````const square = (n) => n * n;
``````

``````square(2); // 4
square(2); // 4
square(2); // 4
// ...
``````

`2`作为square函数的参数传递始终会返回`4`。这样咱们可以把`square(2)`换成`4`，我们的函数就是引用透明的。

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

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

`sum(5, 8)` 总等于`13`，所以可以做些骚操作：

``````sum(3, 13);
``````

## 函数是 JS 中的一级公民

• 从常量和变量中引用它。
• 将其作为参数传递给其他函数。
• 作为其他函数的结果返回它。

``````const doubleSum = (a, b) => (a + b) * 2;
``````

``````const doubleSubtraction = (a, b) => (a - b) * 2;
``````

``````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
``````

`f`参数并用它来处理`a``b`， 这里传递了`sum`函数和`subtraction`并使用`doubleOperator`函数进行组合并创建新行为。

• 将一个或多个函数作为参数
• 返回一个函数作为结果

## 命令式

• 创建一个空数组`evenNumbers`
• 遍历数组 `numbers`
• 将偶数 push 到`evenNumbers`数组中
``````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 = n => n % 2 == 0;
const listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
listOfNumbers.filter(even); // [0, 2, 4, 6, 8, 10]

``````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); // [2, 1, 0]
``````

`smaller`的函数中使用 `this`,一开始看起来有点奇怪，但是很容易理解。

`filter`函数中的第二个参数表示上面 `this`， 也就是 `x` 值。

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

``````const olderThan21 = person => person.age > 21;
const overAge = people => people.filter(olderThan21);
overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }]

``````

## map

`map`函数的主要思路是转换集合。

`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]
``````

``````Math.abs(-1); // 1
Math.abs(1); // 1
Math.abs(-2); // 2
Math.abs(2); // 2
``````

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

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

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

## Reduce

`reduce`函数的思想是接收一个函数和一个集合，并返回通过组合这些项创建的值。

``````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
``````

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

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

getTotalAmount(shoppingCart); // 120
``````

`getAmount`接收`product`对象并只返回`amount`值，即`[10,30,20,60]`，然后，`reduce`通过相加将所有项组合起来。

## 三个函数的示例

``````let shoppingCart = [
{ productTitle: "Functional Programming", type: "books", amount: 10 },
{ productTitle: "Kindle", type: "eletronics", amount: 30 },
{ productTitle: "Shoes", type: "fashion", amount: 20 },
{ productTitle: "Clean Code", type: "books", amount: 60 }
]
``````

• 过滤 type 为 books的
• 使用`map`将购物车转换为`amount`集合。
• `reduce`将所有项加起来。
``````let shoppingCart = [
{ productTitle: "Functional Programming", type: "books", amount: 10 },
{ productTitle: "Kindle", type: "eletronics", amount: 30 },
{ productTitle: "Shoes", type: "fashion", amount: 20 },
{ productTitle: "Clean Code", type: "books", amount: 60 }
]

const byBooks = (order) => order.type == "books";
const getAmount = (order) => order.amount;
const sumAmount = (acc, amount) => acc + amount;

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

getTotalAmount(shoppingCart); // 70

``````

## 交流

https://github.com/qq44924588...

2 条评论