博客

JavaScript Functional Programming:组合函数 Composition

组合(Composition)函数,就是把两个或以上的函数组合到一块儿,整成一个新的函数。我找到了一个很好的例子,很好地解释了组合函数这个概念。

比如一个应用主要是记录一下日常的花销(expenses),应用里的数据看起来像这样:

const expenses = [
  {
    name: '租金',
    price: 3000,
    type: '日常'
  }, 
  {
    name: '阿里云服务',
    price: 600,
    type: '服务'
  }, 
  {
    name: '健身中心',
    price: 50,
    type: '健康'
  }, 
  {
    name: '水电',
    price: 100,
    type: '日常'
  }
];

合计花销

现在我要合计一下所有花销,创建一个函数,用一下 map 与 reduce,这个函数像这样:

const sum = (source) => 
  source
    .map((item) => item.price)
    .reduce((accumulator, price) => accumulator + price, 0)

sum 这个函数接收一个 source 参数,在函数里,先用 map,返回 source 里的所有的 price(价格)。然后再用 reduce 去处理返回的 price ,这里就是合计所有的 price 的值。

这个函数用起来像这样:

let total = sum(expenses) // 结果:3750

JavaScript Functional Programming:不变性 Immutability

不变性(immutability),指的是一个东西被创建以后就不会发生变化了。函数式编程里的东西一般都具有这种特性。让对象具有不变性是一件好事,如果对象需要发生改变,你应该去创建一个新的对象,在新的对象里包含发生改变的部分,不要直接去修改对象本身。

下面这几个单词经常会出现:

  1. immutability:名词,不变性。表示东西不会发生改变的这种特性。
  2. immutable:形容词,不可变。可以说一个东西 immutable ,表示这个东西不会发生变化。
  3. mutability:名词,可变性,突变性,易变性。表示东西会发生改变的这种特性。
  4. mutate:动词,突变。
  5. mutable:形容词,可变。

在 JavaScript 里面,字符串与数字都具有不变性。也就是一个字符串一旦被创建,它的值就不会发生变化。但是 JavaScript 里的数组或对象是可变的,让它们不可变可以使用 Object.freeze 冻结一下它们(只能冻一层),也可以使用一些外部库,比如 Immutable.js。

实验一下:

let name = 'ninghao.net'
undefined

name[7]
"."

name[7] = '-'
"-"

name
"ninghao.net"

在上面尝试修改 name 的值(把 ninghao.net 里的点“ . ” 换成 “ - ” ),办不到。因为字符串这种值不能被改变。

JavaScript Functional Programming:Currying

Currying 指的就是把一个接受多个参数的函数,搞成每次只接收一个参数的函数序列。

看个例子:

const greet = (greeting, name) => {
  return `${greeting}, ${name}`
}

greet('hello', 'ninghao') // “hello, ninghao”

上面的 greet 就是一个接收多个参数的函数。如果把它转换成 Currying 风格的函数,会像这样:

const greet = greeting => name => `${greeting}, ${name}`

上面用了箭头函数,如果写成普通的函数应该像这样:

JavaScript Functional Programming:高阶函数 Higher order functions

高阶函数(higher-order functions),就是返回其它函数的函数,或者使用其它函数作为它的参数的函数。

使用函数作为参数

因为函数本身就是一个值,所以可以让函数作为参数传递给其它的函数。JavaScript 有些函数就需要用到函数类型的参数,比如 Array.map。

比如我有一组数据:

const names = ['小猫', '小狗', '小刺猬']

我要分别问候一下这组数据里的每个项目:

const greetings = names.map(function(name) {
  return `hi ~ ${name}`
})

console.log(greetings)
// ["hi ~ 小猫", "hi ~ 小狗", "hi ~ 小刺猬"]

上面的 map 方法里用了一个匿名函数作为它的参数。在这个函数里面,我们在数组里的每个项目的前面都加上了一个 “hi ~” ,map 会返回一个新的数组,这个数组我交给了 greetings 变量。

JavaScript Functional Programming:声明式与命令式

函数式编程属于声明式编程(declarative programming)的范畴,经常跟声明式编程一块儿讨论的是命令式编程(imperative programming),因为它们是两种不太一样的风格。

命令式编程一般就是说清楚具体要怎么样得到一个结果:先这样做,再这样做,然后再这样,如果这样,就这样做 ...  声明式编程就是声明(说明)一下你想得到的结果是什么样的:把这组电影里的平均分大于 9 分的电影过滤出来给我。

比如有一组电影,你想过滤出评分 9 分以上的电影。

let movies = [
  { title: 'The Shawshank Redemption', rating: 9.6 },
  { title: 'Forrest Gump', rating: 9.4 },
  { title: 'Roman Holiday', rating: 8.9 }
]

第 12 周课程与内容简报

JavaScript 函数式编程系列博客,更新了定制 Drupal 界面的课程,新发布了 Socket.io 的使用课程。

JavaScript Functional Programming:纯函数

函数式编程鼓励我们多创建纯函数(pure functions),纯函数只依赖你交给它的东西,不使用任何函数以外的东西,也不会影响到函数以外的东西。跟纯函数对应的就是不纯函数(impure functions),也就是不纯函数可能会使用函数以外的东西,比如使用了一个全局变量。也可能会影响到函数以外的东西,比如改变了一个全局变量的值。

多使用纯属函数是因为它更可靠一些,也没什么副作用(side effects)。你交给它同样的值,它每次都会给你输出同样的结果,这种特质叫所指透明(Referential transparency) 。这会让程序更稳定,也更容易测试。

副作用

纯函数没副作用,有副作用的函数都不纯。我吃了一片感冒药,是要治我的感冒,但副作用是它让我想睡觉。函数的副作用多数表现为函数依赖或者改变了它以外的东西。

看个例子:

let name = 'ninghao'
const greet = () => {
  console.log(`hello, ${name}`)
}

greet 不是纯函数,因为这个函数依赖函数以外的东西,这里就是全局作用域下的 name。这样做的问题是,函数依赖的 name 很可能在应用运行的时候发生变化,这样试一下:

JavaScript Functional Programming:作用域

全局作用域

打开一个 js 文件,写了一行代码,这行代码所在的位置就会是全局作用域(global scope)。比如:

var name = '狗狗'

局部作用域

全局作用域只有一个,在全局使用域里面定义的其它的作用域都被称为局部作用域(local scope)。局部作用域是由函数创建的,每个函数都会创建一个局部作用域。

下面我创建了一个名字是 greet 的函数,在它里面声明了一个 name 变量,这个变量是在局部作用域之内。

// 作用域 A: 全局作用域

var greet = function() {
  // 作用域 B:局部作用域
  // 这里是由 greet 这个函数创建的局部作用域

  var name = '猫咪'
}

在这个局部作用域里面定义的东西,在这个作用域的外面是访问不到的。试一下:

JavaScript Functional Programming:定义函数的几种方法

在 JavaScript 语言里,函数是一种对象,所以可以说函数是 JavaScript 里的一等公民(first-class citizens)。

之前我们这样定义过一个函数:

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

因为在 JavaScript 里面是对象(object),所以它会有一些属性还有方法。比如 name 属性是函数的名字,length 属性指的是函数里面有多少个必须要传递的参数。比如访问上面定义的这个函数里的两个属性:

greet.name
// 输出 greet

greet.length
// 输出 2,表示有两个参数

函数都是 Function 的实例,所以你可以访问 Function 上定义的属性:name,length,可以使用方法:apply(),call()。可以这样证明:

JavaScript Functional Programming:函数

Functional Programming,函数式编程。是一套编写程序的方法,也可以说是一种风格。

函数

先了解一下什么是 Function,函数。来看一行代码:

console.log('hello ~')

执行上面代码会在控制台上输出一个 “hello ~”,每次你想在控制台上输出一个 “hello ~”,你都要执行一下上面这行代码。我们可以用一个函数包装一下这行代码:

function greet() {
  console.log('hello ~')
}

定义函数可以使用 function 这个关键词,上面我们定义了一个名字是 greet(问候的意思) 的函数。执行 / 调用这个函数,就会在控制台上输出一个 “hello ~”,因为这是这个函数要做的事情。调用函数可以在函数的名字的后面加上一组括号(),像这样:

greet()

在我们的程序里,可以重复使用这个函数。

参数

让函数更灵活一点,可以在定义它的时候去设置一些它能接受的参数,这些参数你可以在函数的内部去使用,在调用函数的时候,你可以指定函数的参数的值。像这样再改进一下 greet 这个函数:



微信好友

用微信扫描二维码,
加我好友。



微信公众号

用微信扫描二维码,
订阅宁皓网公众号。



240746680

用 QQ 扫描二维码,
加入宁皓网 QQ 群。

统计

7291
分钟
0
你学会了
0%
完成

社会化网络

关于

微信订阅号

扫描微信二维码关注宁皓网,每天进步一点