javascript函数式
After a long time learning and working with object-oriented programming, I took a step back to think about system complexity.
经过长时间的学习和使用面向对象的编程,我退后了一步来思考系统的复杂性。
“Complexity is anything that makes software hard to understand or to modify.
" — John Outerhout
“Complexity is anything that makes software hard to understand or to modify.
“ —约翰·奥特豪特
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.
通过研究,我发现了函数式编程概念,例如不变性和纯函数。 这些概念使您能够构建无副作用的功能,因此更易于维护系统-还有其他好处 。
In this post, I will tell you more about functional programming, and some important concepts, with a lot of code examples in JavaScript.
在本文中,我将通过JavaScript的许多代码示例向您详细介绍函数式编程和一些重要概念。
什么是函数式编程? (What is functional programming?)
Functional 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
函数式编程是一种编程范例-一种构建计算机程序的结构和元素的风格-将计算视为对数学函数的评估,并且避免了状态和可变数据的更改- 维基百科
纯功能 (Pure functions)
The first fundamental concept we learn when we want to understand functional programming is pure functions. But what does that really mean? What makes a function pure?
当我们想了解函数式编程时,我们学习的第一个基本概念是纯函数 。 但这到底是什么意思? 是什么使函数纯净?
So how do we know if a function is pure
or not? Here is a very strict definition of purity:
那么我们如何知道一个函数是否pure
呢? 这是一个非常严格的纯度定义:
It returns the same result if given the same arguments (it is also referred as
deterministic
)如果给定相同的参数,它将返回相同的结果(也称为
deterministic
)- It does not cause any observable side effects 它不会引起任何明显的副作用
如果给定相同的参数,它将返回相同的结果 (It returns the same result if given the same arguments)
Imagine we want to implement a function that calculates the area of a circle. An impure function would receive radius
as the parameter, and then calculate radius * radius * PI
:
假设我们要实现一个计算圆的面积的函数。 一个不纯函数将接收radius
作为参数,然后计算radius * radius * PI
:
let PI = 3.14;const calculateArea = (radius) => radius * radius * PI;calculateArea(10); // returns 314.0
Why is this an impure function? Simply because it uses a global object that was not passed as a parameter to the function.
为什么这是不纯功能? 仅仅是因为它使用了未作为参数传递给函数的全局对象。
Now imagine some mathematicians argue that the PI
value is actually 42
and change the value of the global object.
现在想象一些数学家认为PI
值实际上是42
并且会更改全局对象的值。
Our impure function will now result in 10 * 10 * 42
= 4200
. For the same parameter (radius = 10
), we have a different result.
我们的不纯函数现在将导致10 * 10 * 42
= 4200
。 对于相同的参数( radius = 10
),我们得到不同的结果。
Let's fix it!
让我们修复它!
let PI = 3.14;const calculateArea = (radius, pi) => radius * radius * pi;calculateArea(10, PI); // returns 314.0
Now we’ll always pass the value of PI
as a parameter to the function. So now we are just accessing parameters passed to the function. No external object
.
现在,我们将始终将PI
的值作为参数传递给函数。 因此,现在我们只访问传递给函数的参数。 没有external object
。
For the parameters
radius = 10
andPI = 3.14
, we will always have the same the result:314.0
对于参数
radius = 10
和PI = 3.14
,我们将始终具有相同的结果:314.0
For the parameters
radius = 10
andPI = 42
, we will always have the same the result:4200
对于参数
radius = 10
和PI = 42
,我们将始终具有相同的结果:4200
读取文件 (Reading Files)
If our function reads external files, it’s not a pure function — the file’s contents can change.
如果我们的函数读取外部文件,则它不是纯粹的函数-文件的内容可以更改。
const charactersCounter = (text) => `Character count: ${text.length}`;function analyzeFile(filename) {let fileContent = open(filename);return charactersCounter(fileContent);
}
随机数生成 (Random number generation)
Any function that relies on a random number generator cannot be pure.
任何依赖随机数生成器的函数都不能是纯函数。
function yearEndEvaluation() {if (Math.random() > 0.5) {return "You get a raise!";} else {return "Better luck next year!";}
}
它不会引起任何明显的副作用 (It does not cause any observable side effects)
Examples of observable side effects include modifying a global object or a parameter passed by reference.
可观察到的副作用的示例包括修改全局对象或通过引用传递的参数。
Now we want to implement a function to receive an integer value and return the value increased by 1.
现在我们要实现一个函数,以接收一个整数值并返回增加了1的值。
let counter = 1;function increaseCounter(value) {counter = value + 1;
}increaseCounter(counter);
console.log(counter); // 2
We have the counter
value. Our impure function receives that value and re-assigns the counter with the value increased by 1.
我们有对counter
。 我们的不纯函数会收到该值,然后将值增加1的值重新分配给计数器。
let counter = 1;const increaseCounter = (value) => value + 1;increaseCounter(counter); // 2
console.log(counter); // 1
Observation: mutability is discouraged in functional programming.
观察 :在函数式编程中不鼓励可变性。
We are modifying the global object. But how would we make it pure
? Just return the value increased by 1.
我们正在修改全局对象。 但是,我们将如何使其pure
呢? 只需返回增加1的值即可。
See that our pure function increaseCounter
returns 2, but the counter
value is still the same. The function returns the incremented value without altering the value of the variable.
看到我们的纯函数increaseCounter
返回2,但是counter
值仍然相同。 该函数将返回增加的值,而不更改变量的值。
If we follow these two simple rules, it gets easier to understand our programs. Now every function is isolated and unable to impact other parts of our system.
如果我们遵循这两个简单的规则,就会更容易理解我们的程序。 现在,每个功能都是孤立的,无法影响系统的其他部分。
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. We don’t need to mock anything. So we can unit test pure functions with different contexts:
该代码绝对更容易测试。 我们不需要嘲笑任何东西。 因此,我们可以对具有不同上下文的纯函数进行单元测试:
Given a parameter
A
→ expect the function to return valueB
给定参数
A
→期望函数返回值B
Given a parameter
C
→ expect the function to return valueD
给定参数
C
→期望函数返回值D
A simple example would be a function to receive a collection of numbers and expect it to increment each element of this collection.
一个简单的示例是一个函数,该函数接收一个数字集合,并期望它增加该集合的每个元素。
let list = [1, 2, 3, 4, 5];const incrementNumbers = (list) => list.map(number => number + 1);
We receive the numbers
array, use map
to increment each number, and return a new list of incremented numbers.
我们收到numbers
数组,使用map
递增每个数字,并返回一个新的递增数字列表。
incrementNumbers(list); // [2, 3, 4, 5, 6]
For the input
[1, 2, 3, 4, 5]
, the expected output
would be [2, 3, 4, 5, 6]
.
对于input
[1, 2, 3, 4, 5]
,预期output
为[2, 3, 4, 5, 6]
。
不变性 (Immutability)
Unchanging over time or unable to be changed.
随时间不变或无法更改。
When 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.
当数据是不可变的时,它的 状态无法改变 创建之后。 如果要更改不可变对象,则不能。 代替, 您将使用新值创建一个新对象。
In JavaScript we commonly use the for
loop. This next for
statement has some mutable variables.
在JavaScript中,我们通常使用for
循环。 接下来的for
语句具有一些可变变量。
var values = [1, 2, 3, 4, 5];
var sumOfValues = 0;for (var i = 0; i < values.length; i++) {sumOfValues += values[i];
}sumOfValues // 15
For each iteration, we are changing the i
and the sumOfValue
state. But how do we handle mutability in iteration? Recursion.
对于每次迭代,我们都将更改i
和sumOfValue
状态。 但是,我们如何处理迭代中的可变性呢? 递归。
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
So here we have the sum
function that receives a vector of numerical values. The function calls itself until we get the list empty (our recursion base case
). For each "iteration" we will add the value to the total
accumulator.
因此,这里有sum
函数,用于接收数值向量。 该函数将自行调用,直到列表为空( 递归base case
)为止。 对于每个“迭代”,我们会将其值添加到total
累加器中。
With recursion, we keep our variables immutable. The list
and the accumulator
variables are not changed. It keeps the same value.
通过递归,我们保留变量 一成不变的。 list
和accumulator
变量不变。 它保持相同的值。
Observation: We can use reduce
to implement this function. We will cover this in the higher order functions topic.
观察 :我们可以使用reduce
来实现此功能。 我们将在高阶函数主题中对此进行介绍。
It is also very common to build up the final state of an object. Imagine we have a string, and we want to transform this string into a url slug
.
建立对象的最终状态也很常见。 假设我们有一个字符串,并且我们想将此字符串转换为url slug
。
In Object Oriented Programming in Ruby, we would create a class, let’s say, UrlSlugify
. And this class will have a slugify
method to transform the string input into a url slug
.
在Ruby中的面向对象编程中,我们将创建一个类,例如UrlSlugify
。 此类将具有slugify
方法,以将输入的字符串转换为url slug
。
class UrlSlugifyattr_reader :textdef initialize(text)@text = textenddef slugify!text.downcase!text.strip!text.gsub!(' ', '-')end
endUrlSlugify.new(' I will be a url slug ').slugify! # "i-will-be-a-url-slug"
It’s implemented!
它实现了!
Here we have imperative programming saying exactly what we want to do in each slugify
process — first lower-case, then remove useless white spaces and, finally, replace remaining white spaces with hyphens.
在这里,我们必须进行命令式编程,确切地说出每个slugify
处理中要执行的操作-首先小写字母,然后删除无用的空格,最后用连字符替换其余的空格。
But we are mutating the input state in this process.
但是我们在这个过程中正在改变输入状态。
We can handle this mutation by doing function composition, or function chaining. In other words, the result of a function will be used as an input for the next function, without modifying the original input string.
我们可以通过执行功能组合或功能链接来处理此突变。 换句话说,函数的结果将用作下一个函数的输入,而无需修改原始输入字符串。
const string = " I will be a url slug ";const slugify = string =>string.toLowerCase().trim().split(" ").join("-");slugify(string); // i-will-be-a-url-slug
Here we have:
这里有:
toLowerCase
: converts the string to all lower casetoLowerCase
:将字符串转换为所有小写trim
: removes white-space from both ends of a stringtrim
:删除字符串两端的空白split
andjoin
: replaces all instances of match with replacement in a given stringsplit
andjoin
:用给定字符串中的替换替换所有match实例
We combine all these 4 functions and we can "slugify"
our string.
我们将所有这四个函数结合在一起,就可以"slugify"
字符串了。
参照透明 (Referential transparency)
Let’s implement a square function
:
让我们实现一个square function
:
const square = (n) => n * n;
This pure function will always have the same output, given the same input.
给定相同的输入,此纯函数将始终具有相同的输出。
square(2); // 4
square(2); // 4
square(2); // 4
// ...
Passing 2
as a parameter of the square function
will always returns 4. So now we can replace the square(2)
with 4. Our function is referentially transparent
.
传递2
作为square function
的参数将始终返回4。因此,现在我们可以将square(2)
替换为4。我们的函数是referentially transparent
。
Basically, if a function consistently yields the same result for the same input, it is referentially transparent.
基本上,如果一个函数对于相同的输入始终产生相同的结果,则它是参照透明的。
pure functions + immutable data = referential transparency
纯函数+不可变数据=参考透明
With this concept, a cool thing we can do is to memoize the function. Imagine we have this function:
有了这个概念,我们可以做的一件很酷的事情就是记住该功能。 想象一下我们具有以下功能:
const sum = (a, b) => a + b;
And we call it with these parameters:
我们用以下参数调用它:
sum(3, sum(5, 8));
The sum(5, 8)
equals 13
. This function will always result in 13
. So we can do this:
sum(5, 8)
等于13
。 此功能将始终导致13
。 因此,我们可以这样做:
sum(3, 13);
And this expression will always result in 16
. We can replace the entire expression with a numerical constant and memoize it.
这个表达式将始终为16
。 我们可以将整个表达式替换为数字常量并进行记忆 。
作为一流实体 (Functions as first-class entities)
The idea of functions as first-class entities is that functions are also treated as values and used as data.
函数作为一等实体的想法是将函数也视为值并用作数据。
Functions 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 作为其他函数的结果返回
The idea is to treat functions as values and pass functions like data. This way we can combine different functions to create new functions with new behavior.
这个想法是将函数视为值,并像数据一样传递函数。 这样,我们可以组合不同的功能以创建具有新行为的新功能。
Imagine we have a function that sums two values and then doubles the value. Something like this:
想象一下,我们有一个将两个值相加然后将值加倍的函数。 像这样:
const doubleSum = (a, b) => (a + b) * 2;
Now a function that subtracts values and the returns the double:
现在,一个减去值并返回双精度值的函数:
const doubleSubtraction = (a, b) => (a - b) * 2;
These functions have similar logic, but the difference is the operators functions. If we can treat functions as values and pass these as arguments, we can build a function that receives the operator function and use it inside our function.
这些功能具有相似的逻辑,但是区别在于运算符功能。 如果我们可以将函数视为值并将其作为参数传递,则可以构建一个接收操作符函数并在函数内部使用的函数。
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
Now we have an f
argument, and use it to process a
and b
. We passed the sum
and subtraction
functions to compose with the doubleOperator
function and create a new behavior.
现在我们有一个f
参数,并用它来处理a
和b
。 我们传递了sum
和subtraction
函数来与doubleOperator
函数组合并创建新行为。
高阶函数 (Higher-order functions)
When we talk about higher-order functions, we mean a function that either:
当我们谈论高阶函数时,我们指的是以下函数之一:
- takes one or more functions as arguments, or 将一个或多个函数作为参数,或
- returns a function as its result 返回一个函数作为其结果
The doubleOperator
function we implemented above is a higher-order function because it takes an operator function as an argument and uses it.
我们上面实现的doubleOperator
函数是一个高阶函数,因为它将运算符作为参数并使用它。
You’ve probably already heard about filter
, map
, and reduce
. Let's take a look at these.
您可能已经听说过filter
, map
和reduce
。 让我们来看看这些。
过滤 (Filter)
Given a collection, we want to filter by an attribute. The filter function expects a true
or false
value to determine if the element should or should not be included in the result collection. Basically, if the callback expression is true
, the filter function will include the element in the result collection. Otherwise, it will not.
给定一个集合,我们想按属性过滤。 过滤器函数期望使用true
或false
值来确定是否应将元素包含在结果集合中。 基本上,如果回调表达式为true
,则过滤器函数会将元素包括在结果集合中。 否则,它将不会。
A simple example is when we have a collection of integers and we want only the even numbers.
一个简单的例子是当我们有一个整数集合并且我们只需要偶数时。
势在必行 (Imperative approach)
An imperative way to do it with JavaScript is to:
使用JavaScript的一种必要方法是:
create an empty array
evenNumbers
创建一个空数组
evenNumbers
iterate over the
numbers
array遍历
numbers
数组push the even numbers to the
evenNumbers
array将偶数推送到
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]
We can also use the filter
higher order function to receive the even
function, and return a list of even numbers:
我们还可以使用filter
高阶函数来接收even
函数,并返回偶数列表:
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]
One interesting problem I solved on Hacker Rank FP Path was the Filter Array problem. The problem idea is to filter a given array of integers and output only those values that are less than a specified value X
.
我在Hacker Rank FP路径上解决的一个有趣的问题是“ 过滤器阵列”问题 。 问题的思想是过滤给定的整数数组,仅输出小于指定值X
那些值。
An imperative JavaScript solution to this problem is something like:
解决此问题的强制性JavaScript解决方案如下:
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]
We say exactly what our function needs to do — iterate over the collection, compare the collection current item with x
, and push this element to the resultArray
if it pass the condition.
我们确切地说出函数需要做的事情–遍历集合,将集合当前项与x
进行比较,如果该元素通过条件,则将其推送到resultArray
。
声明式方法 (Declarative approach)
But we want a more declarative way to solve this problem, and using the filter
higher order function as well.
但是,我们需要一种更具声明性的方式来解决此问题,并且还需要使用filter
高阶函数。
A declarative JavaScript solution would be something like this:
声明性JavaScript解决方案如下所示:
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]
Using this
in the smaller
function seems a bit strange in the first place, but is easy to understand.
首先,在smaller
函数中使用this
函数似乎有些奇怪,但很容易理解。
this
will be the second parameter in the filter
function. In this case, 3
(the x
) is represented by this
. That's it.
this
将是filter
功能中的第二个参数。 在这种情况下, 3
( x
)表示为this
。 而已。
We can also do this with maps. Imagine we have a map of people with their name
and age
.
我们也可以使用地图来做到这一点。 想象一下,我们有一幅地图,上面有他们的name
和age
。
let people = [{ name: "TK", age: 26 },{ name: "Kaio", age: 10 },{ name: "Kazumi", age: 30 }
];
And we want to filter only people over a specified value of age, in this example people who are more than 21 years old.
而且,我们只希望过滤特定年龄段的人员,在此示例中,年龄超过21岁的人员。
const olderThan21 = person => person.age > 21;
const overAge = people => people.filter(olderThan21);
overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }]
Summary of code:
代码摘要:
we have a list of people (with
name
andage
).我们有一个人的名单(
name
和age
)。we have a function
olderThan21
. In this case, for each person in people array, we want to access theage
and see if it is older than 21.我们有一个功能
olderThan21
。 在这种情况下,对于人员数组中的每个人,我们要访问age
并查看age
是否大于21岁。- we filter all people based on this function. 我们基于此功能过滤所有人员。
地图 (Map)
The idea of map is to transform a collection.
map的想法是转换集合。
The
map
method transforms a collection by applying a function to all of its elements and building a new collection from the returned values.
map
方法通过将函数应用于其所有元素并根据返回的值构建新集合来转换集合。
Let’s get the same people
collection above. We don't want to filter by “over age” now. We just want a list of strings, something like TK is 26 years old
. So the final string might be :name is :age years old
where :name
and :age
are attributes from each element in the people
collection.
让我们得到上面的同一people
集合。 我们现在不想按“年龄超过”进行过滤。 我们只想要一个字符串列表,例如TK is 26 years old
。 因此,最后一个字符串可能是:name is :age years old
,其中:name
和:age
是people
集合中每个元素的属性。
In a imperative JavaScript way, it would be:
用命令式JavaScript方式,它将是:
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']
In a declarative JavaScript way, it would be:
用声明性JavaScript方式,它将是:
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']
The whole idea is to transform a given array into a new array.
整个想法是将给定的数组转换为新的数组。
Another interesting Hacker Rank problem was the update list problem. We just want to update the values of a given array with their absolute values.
另一个有趣的Hacker Rank问题是更新列表问题 。 我们只想用其绝对值更新给定数组的值。
For example, the input [1, 2, 3, -4, 5]
needs the output to be [1, 2, 3, 4, 5]
. The absolute value of -4
is 4
.
例如,输入[1, 2, 3, -4, 5]
需要将输出为[1, 2, 3, 4, 5]
。 -4
的绝对值为4
。
A simple solution would be an in-place update for each collection value.
一个简单的解决方案是就每个集合值进行就地更新。
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]
We use the Math.abs
function to transform the value into its absolute value, and do the in-place update.
我们使用Math.abs
函数将值转换为其绝对值,并进行就地更新。
This is not a functional way to implement this solution.
这不是实现此解决方案的功能方法。
First, we learned about immutability. We know how immutability is important to make our functions more consistent and predictable. The idea is to build a new collection with all absolute values.
首先,我们了解了不变性。 我们知道不变性对于使我们的功能更加一致和可预测是多么重要。 这个想法是建立一个具有所有绝对值的新集合。
Second, why not use map
here to "transform" all data?
其次,为什么不使用map
来“转换”所有数据?
My first idea was to test the Math.abs
function to handle only one value.
我的第一个想法是测试Math.abs
函数以仅处理一个值。
Math.abs(-1); // 1
Math.abs(1); // 1
Math.abs(-2); // 2
Math.abs(2); // 2
We want to transform each value into a positive value (the absolute value).
我们希望将每个值转换为正值(绝对值)。
Now that we know how to do absolute
for one value, we can use this function to pass as an argument to the map
function. Do you remember that a higher order function
can receive a function as an argument and use it? Yes, map can do it!
现在我们知道如何对一个值进行absolute
运算,我们可以使用此函数作为参数传递给map
函数。 您还记得higher order function
可以将函数作为参数来使用吗? 是的,地图可以做到!
let values = [1, 2, 3, -4, 5];const updateListMap = (values) => values.map(Math.abs);updateListMap(values); // [1, 2, 3, 4, 5]
Wow. So beautiful!
哇。 如此美丽!
减少 (Reduce)
The idea of reduce is to receive a function and a collection, and return a value created by combining the items.
reduce的想法是接收一个函数和一个集合,并返回通过组合项目创建的值。
A common example people talk about is to get the total amount of an order. Imagine you were at a shopping website. You’ve added Product 1
, Product 2
, Product 3
, and Product 4
to your shopping cart (order). Now we want to calculate the total amount of the shopping cart.
人们谈论的一个常见示例是获取订单的总金额。 想象一下您在一个购物网站上。 您已将Product 1
, Product 2
, Product 3
和Product 4
到购物车(订单)。 现在,我们要计算购物车的总金额。
In imperative way, we would iterate the order list and sum each product amount to the total amount.
以命令方式,我们将迭代订单清单,并将每个产品的数量加总到总数量。
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
Using reduce
, we can build a function to handle the amount sum
and pass it as an argument to the reduce
function.
使用reduce
,我们可以构建一个函数来处理amount sum
并将其作为参数传递给reduce
函数。
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
Here we have shoppingCart
, the function sumAmount
that receives the current currentTotalAmount
, and the order
object to sum
them.
在这里,我们有shoppingCart
,接收当前currentTotalAmount
的功能sumAmount
以及用于对它们sum
的order
对象。
The getTotalAmount
function is used to reduce
the shoppingCart
by using the sumAmount
and starting from 0
.
getTotalAmount
函数用于通过使用sumAmount
并从0
开始reduce
shoppingCart
sumAmount
。
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
转换为amount
值的集合,然后仅将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
The getAmount
receives the product object and returns only the amount
value. So what we have here is [10, 30, 20, 60]
. And then the reduce
combines all items by adding up. Beautiful!
getAmount
接收产品对象,并且仅返回amount
值。 所以我们这里是[10, 30, 20, 60]
。 然后reduce
将所有项目相加在一起。 美丽!
We took a look at how each higher order function works. I want to show you an example of how we can compose all three functions in a simple example.
我们看了每个高阶函数如何工作。 我想向您展示一个示例,说明如何在一个简单的示例中组合所有三个函数。
Talking about shopping cart
, imagine we have this list of products in our order:
谈论shopping cart
,想象一下我们的订单中有以下产品清单:
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 }
]
We want the total amount of all books in our shopping cart. Simple as that. The algorithm?
我们需要购物车中所有书籍的总数。 就那么简单。 算法?
- filter by book type 按书籍类型过滤
- transform the shopping cart into a collection of amount using map 使用地图将购物车转化为金额集合
- combine all items by adding them up with reduce 通过将所有项目与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
Done!
做完了!
资源资源 (Resources)
I’ve organised some resources I read and studied. I’m sharing the ones that I found really interesting. For more resources, visit my Functional Programming Github repository
我整理了一些阅读和学习的资源。 我正在分享我发现非常有趣的内容。 有关更多资源,请访问我的Functional Programming Github存储库
EcmaScript 6 course by Wes Bos
Wes Bos的EcmaScript 6课程
JavaScript by OneMonth
JavaScript by OneMonth
Ruby specific resources
Ruby专用资源
Javascript specific resources
Javascript专用资源
Clojure specific resources
Clojure特定资源
Learn React by building an App
通过构建应用来学习React
简介 (Intros)
Learning FP in JS
在JS中学习FP
Intro do FP with Python
用Python介绍FP
Overview of FP
FP概述
A quick intro to functional JS
功能性JS快速入门
What is FP?
什么是FP?
Functional Programming Jargon
函数式编程术语
纯功能 (Pure functions)
What is a pure function?
什么是纯函数?
Pure Functional Programming 1
纯函数式编程1
Pure Functional Programming 2
纯函数式编程2
不变的数据 (Immutable data)
Immutable DS for functional programming
用于功能编程的不可变DS
Why shared mutable state is the root of all evil
为什么共有的可变状态是万恶之源
高阶函数 (Higher-order functions)
Eloquent JS: Higher Order Functions
雄辩的JS:高阶函数
Fun fun function Filter
趣味功能过滤器
Fun fun function Map
趣味功能图
Fun fun function Basic Reduce
趣味功能基本减少
Fun fun function Advanced Reduce
趣味功能高级减少
Clojure Higher Order Functions
Clojure高阶函数
Purely Function Filter
纯功能过滤器
Purely Functional Map
纯功能图
Purely Functional Reduce
纯功能减少
声明式编程 (Declarative Programming)
Declarative Programming vs Imperative
声明式编程与命令式
而已! (That’s it!)
Hey people, I hope you had fun reading this post, and I hope you learned a lot here! This was my attempt to share what I’m learning.
大家好,我希望您在阅读这篇文章时玩得开心,希望您在这里学到了很多东西! 这是我分享自己所学知识的尝试。
Here is the repository with all codes from this article.
这是本文中所有代码的存储库 。
Come learn with me. I’m sharing resources and my code in this Learning Functional Programming repository.
来跟我学习。 我正在这个“ 学习功能编程”存储库中共享资源和代码。
I also wrote an FP post but using mainly Clojure
我还写了一篇FP帖子,但主要使用Clojure
I hope you saw something useful to you here. And see you next time! :)
希望您在这里看到了对您有用的东西。 下次见! :)
My Twitter & Github.
我的Twitter和Github 。
TK.
TK。
翻译自: https://www.freecodecamp.org/news/functional-programming-principles-in-javascript-1b8fc6c3563f/
javascript函数式