函数式编程:用来描述数据之间的映射
思维方式:实现世界的事物与事物之间的联系抽象到函数世界,通过封装、多态和继承来演示事物的联系
理解起来太费脑,有几个例子看一下函数式编程的特点及优势
首先常见的有函数作为参数,函数作为返回值,函数可以存储在变量中(即JS世界中函数一等公民)
1.函数作为参数
// forEach
function forEach (array, fn) {
for(let i=0;i<array.length;i++){
fn(array[i])
}
}
2.函数作为返回值
// once jq中的once只执行一次
function once (fn) {
let done = false
return function () {
if (!done) {
done = true
return fn.apply(this, arguments)
}
}
上述方法的使用,抽象出了我门需要的功能而不是每当我们遇到一个问题就写一遍函数实现,这也做的主要意义抽象了细节,只需要我们关注目标,这种函数中调用函数的方式也称作高阶函数。
这里再介绍另外一个概念:闭包
本质:函数在执行的时候会放在一个执行栈上(关于函数的执行,我会在另外一篇文章里详细写道)当函数执行完毕之后会从执行栈上移除,但是堆上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问外部函数的成员。
下面来看一个闭包的使用案例
// 生成计算数字的多少次幂的函数
function makePower (power) {
return function (x) {
return Math.pow(x, power)
}
}
let power2 = makePower(2)
let power3 = makePower(3)
console.log(power2(4))
上面这个例子是使用闭包的一个很好的封装。
第三介绍会介绍一下纯函数
纯函数:相同的输入永远会输出相同的输出,而且没有任何可观察的副作用
纯函数可以理解为数学中的函数y=f(x)
用例子展示 :数组的slice(纯函数)和splice(不是纯函数)
slice返回数组中的指定部分,不会改变原数组
splice对数组进行操作返回该数组,会改变原数组
使用纯函数的好处:
1.可以缓存:
以下是纯函数缓存的实现
function memoize (f) {
let cache = {}
return function () {
let arg_str = JSON.stringify(arguments)
cache[arg_str] = cache[arg_str] || f.apply(f, arguments)
return cache[arg_str]
}
}
可测试:纯函数让测试更方便
并行处理:在多线程环境下操作共存的内存数据
第四函数柯里化
使用柯里化解决硬编码的问题
eg:函数内部硬编码
function checkAge (age) {
let min = 18
return age >= min
}
//普通纯函数方法
function checkAge (min, age) {
return age >= min
}
checkAge(18, 24)
checkAge(18, 20)
checkAge(20, 30)
// 柯里化
function checkAge (min) {
return function (age) {
return age >= min
}
}
//ES6写法
let chechAge=min=>(age=>age=>min)
let checkAge18 = checkAge(18)
let checkAge20 = checkAge(20)
checkAge18(24)
checkAge18(20)
柯里化:当一个函数有多个参数的时候先传第一部分参数调用它,然后返回一个新的函数接受剩余的参数,返回结果。
lodash中的柯里化案例
const _ = require('lodash')
const match = _.curry(function (reg, str) {
return str.match(reg)
})
const haveSpace = match(/\s+/g)
const haveNumber = match(/\d+/g)
console.log(haveSpace('hello world'))
console.log(haveNumber('25$'))
const filter = _.curry(function (func, array) {
return array.filter(func)
})
console.log(filter(haveSpace, ['John Connor', 'John_Donne']))
const findSpace = filter(haveSpace)
console.log(findSpace(['John Connor', 'John_Donne']))
模拟实现
function curry (func) {
return function curriedFn (...args) {
// 判断实参和形参的个数
if (args.length < func.length) {
return function () {
return curriedFn(...args.concat(Array.from(arguments)))
}
}
// 实参和形参个数相同,调用 func,返回结果
return func(...args)
}
}
总结:
1.柯里化可以让我们给一个函数传第较少的参数得到一个已经记住了某些固定参数的新函数
2.这是一种函数参数的‘缓存’
3.让函数变的更加灵活,让函数的粒度更小
4.可以把多元函数转换成一元函数,可以组合使用函数产生更强大的功能。
第四介 函数组合
函数柯里化如果遇到多个函数嵌套的时候如果其中一个函数在执行中出错,我们是很难发现问题的,这就需要将里面的函数拆开组合起来。就有了函数组合的概念。
函数组合:如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合成一个函数。
我们以lodash的flowRIght方法为例子(输入的函数从右到左执行)
模拟实现:
// 多函数组合
function compose (...fns) {
return function (value) {
return fns.reverse().reduce(function (acc, fn) {
return fn(acc)
}, value)
}
}
// ES6
const compose = (...fns) => value => fns.reverse().reduce((acc, fn) =>
fn(acc), value)
const toUpper = s => s.toUpperCase()
const reverse = arr => arr.reverse()
const first = arr => arr[0]
const f = compose(toUpper, first, reverse)console.log(f(['one', 'two', 'three']))
第五 函子
1.Functor (函子)
正对函数式编程中我们需要把副作用控制在可控的范围内,异常处理,异步操作等因此有就有了函子
Functor
容器:包含值和值的变形关系(函数)
函子:是一个特殊的容器,通过一个普通的对象实现,该对象具有map方法,map方法可以运行一个函数对值进行处理
// 一个容器,包裹一个值
class Container {
// of 静态方法,可以省略 new 关键字创建对象
static of (value) {
return new Container(value)
}
constructor (value) {
this._value = value
}
// map 方法,传入变形关系,将容器里的每一个值映射到另一个容器
map (fn) {
return Container.of(fn(this._value))
}
}
// 测试
Container.of(3)
.map(x => x + 2)
.map(x => x * x)
函子的特点:
1.函数式编程的运算不直接操作值,而是由函子完成
2.函子就是一个实现了 map 契约的对象
3.我们可以把函子想象成一个盒子,这个盒子里封装了一个值
4.想要处理盒子中的值,我们需要给盒子的 map 方法传递一个处理值的函数(纯函数),由这 个函数来对值进行处理
5.最终 map 方法返回一个包含新值的盒子(函子)
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!