(译) 函数式 JS #2: 函数!

close-up photo of factory
"close-up photo of factory" by Taton Moïse on Unsplash

原文连接 By: Krzysztof Czernek程序员

这是 "函数式 JS" 系列的第二篇。查看第一篇编程

简介

如今咱们知道为何学习函数式编程实践能够帮助你成为一个更好的程序员了,那让咱们开始一些有趣的东西吧。数组

在这一部分中,咱们将重点关注与函数式编程相关的术语基本概念缓存

遗憾的是,此次不会涉及不少代码。从好的方面来讲,一旦咱们理解了术语,咱们就可以方便地讨论更复杂的主题。数据结构


函数

毋庸置疑,函数式编程中最重要的就是函数。app

咱们都知道什么是函数。它基本上就是一个(大多数时候都会有一个名字的)代码片断。dom

function add (x, y) {
  return x + y
}
复制代码

然而,当谈到函数式编程时,咱们更想从一个特殊的角度来看待函数:数学中的函数。编程语言

数学?没搞错吧...

即便咱们没必要真的去使用代数,咱们也不得不认可函数式编程深深植根于数学。函数式编程

在简单的数学术语中,函数是一种在给定输入的状况下产生特定输出的机器函数

有趣的是,给定输入只能有一个输出。这意味着,若是咱们为函数提供相同的输入,咱们但愿它始终作一样的事情,而且返回相同的值

这听起来没什么大不了,但实际上这是一个很强的限制。这个数学定义有着深远的影响:

  • 除了输入(参数)以外,函数不能有其余任何依赖
  • 函数必须返回单个值
  • 函数必须是肯定性的(不能使用随机值等)

知足这些标准的函数在编程中称为纯函数,它们对于函数式范式相当重要。

纯函数

让咱们看一下 JavaScript 中的一些函数示例,直接体会一下什么是纯函数。

function coin () {
  return Math.random() < 0.5 ? 'heads' : 'tails'
}
复制代码

Coin 不是纯函数,由于它在给定相同输入(null)的状况下并不老是产生相同的结果 - 它不是肯定性的。

let firstName = 'krzysztof'

function uppercaseName (lastName) {
  return `${firstName.toUpperCase()} ${lastName.toUpperCase()}`
}
复制代码

uppercaseName 不是纯函数,由于它依赖于一个不受其控制的变量。咱们没法肯定在给定相同参数的状况下它总会产生相同的结果。

let user = {
  firstName: 'Krzysztof',
  age: '26'
}

function happyBirthday () {
  user.age = user.age + 1
}
复制代码

happyBirthday不是纯函数,由于它不只访问了一个不受控制的变量,还不会返回任何内容。

function calculatePrice (unitPrice, noOfUnits, couponValue = 0) {
  return unitPrice * noOfUnits - couponValue;
}
复制代码

calculatePrice是纯函数。它不使用任何超出其控制范围的变量,它是肯定性的,咱们能够很是有信心地说它将始终为相同的输入参数组合返回相同的结果。

而后呢?

为何这一切很重要?有如下一些缘由代表纯函数比非纯函数更有优点:

  1. 更易阅读

你只须要读一下它的函数体就知道它作了哪些事情。

  1. 更易理解

不须要查找外部依赖,函数被调用的上下文等。这些对于纯函数都没有任何影响。

  1. 更易测试

若是你想测试一个纯函数,你只须要用一些参数调用它,看看结果是不是你想要的结果。根本无需复杂的设置。

  1. 更高效

若是咱们知道对于给定的输入,函数将始终产生相同的输出,咱们就能够缓存(memoize)它的结果,这样咱们就没必要在每次调用这个函数的时候都从新计算它。

使用纯函数可使代码更易于维护 - 由于它能够更轻松地管理反作用。在接下来的部分中,咱们将了解反作用是什么以及为何,遗憾的是,计算机程序中不可能所有都是纯函数。

如今咱们知道了什么是纯函数,让咱们关注下一个与函数相关的术语:做为一等公民的函数

一等公民函数

与“纯函数”不一样,“一等公民函数”在平常工做中并非一个很实用的概念。可是,在考虑编程语言的特性时,它就颇有用了。

若是在一个编程语言中,函数能够像使用其余的值一样的方式使用,那么你就能够说这个语言具备“一等公民的函数”,也就是说:

  1. 它们能够被传递,
  2. 它们能够被分配给变量,
  3. 它们能够被存储在更复杂的数据结构中,如数组或对象。

能够说没有一等公民的函数,就没有函数式编程(至少会很是的尴尬)。下面这个例子,说明为何函数在 JavaScript 中是一等公民:

function add (a, b) {
  return a + b
}

function multiply (a, b) {
  return a * b
}

const operations = { // 这里咱们把函数当成普通的值使用
  add,
  multiply
}

operations.add(1, 2)
复制代码

正如上面所说,JavaScript 的函数能够在不一样的函数之间传递。可是......这么作的目的是什么呢?

嗯,将函数传入和传出到另一个函数是函数式编程中的常见作法 - 并且功能很是强大。它给咱们引入了...

高阶函数

能够“操做”其余函数的函数被称为高阶函数。这里的操做,意思是指他们能够作到下面两点中的一个或两个:

  • 把其余函数做为参数,
  • 返回一个函数。

这个例子在 JavaScript 世界中很常见。其中一个示例是标准库中的 Array.prototype.map 函数。它须要一个函数做为参数并将其应用于数组中的每一个元素:

const numbers = [1, 1, 2, 3, 5, 8]
const transformFunction = x => x + 2

numbers.map(transformFunction)
复制代码

下面是一个返回函数的函数,这个示例稍显刻意:

function makeGreeter (greeting) {
  return function greet (name) {
    return `${greeting}, ${name}!`
  }
}

// 或者使用 ES6 的语法:

const makeGreeter = greeting => name => `${greeting}, ${name}!`

const greet = makeGreeter('Hello')
console.log(greet('Krzysztof'))
复制代码

你能够看到,这些函数(map 和 makeGreeter)不接受或者返回咱们所知道的那些常规的值。他们在操做函数。

你可能已经熟悉了一些高阶函数,例如:

  • map,
  • reduce,
  • filter,
  • compose,
  • forEach,
  • … 和别的。

函数式编程就是将一些小型,可重用和通用的函数组合成更复杂的函数。所以,在后面的讨论中你将会看到更多不一样的高阶函数。


那么,这就是咱们开始 FP 之旅所需的全部与函数相关的基本术语了。

下一章,咱们将关注函数式编程中的状态 (state) - 如何管理它,以及如何避免它带来的问题等等。咱们已经提到过一些关于状态的内容(在讨论纯函数的时候),后面还有更多!

咱们已经学到了很多东西,但愿你和我同样对下一章感到兴奋!

相关文章
相关标签/搜索