javascript函数式编程

javascript函数式编程

技术杂谈小彩虹2021-07-08 21:59:48100A+A-

函数式编程:用来描述数据之间的映射

思维方式:实现世界的事物与事物之间的联系抽象到函数世界,通过封装、多态和继承来演示事物的联系

理解起来太费脑,有几个例子看一下函数式编程的特点及优势

首先常见的有函数作为参数,函数作为返回值,函数可以存储在变量中(即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 方法返回一个包含新值的盒子(函子)

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们