通过这次课程的学习,加深高级函数编程能力并且通过lodash函数式编程库的使用和原理的解析。更好的理解函数式编程,做流程笔记更好的回顾和重复学习。
一、函数式编程
- 流行库的源码的函数式编程(vue3、react)
- 函数式编程可以摒弃this
- 更好的过滤无用代码,同时方便测试、方便并行处理
- 有一些函数式编程库更好的进行函数式开发:lodash、underscore、ramda
代码理解函数式/非函数
前置知识:函数是一等公民、高阶函数、闭包;
1. 函数是一等公民
2. 函数可以储存在变量中
函数是一等公民是学习高阶函数、函数柯里化的基础。
二、高阶函数
1. 可以把函数作为参数传递给另一个函数
2. 可以把函数作为另一个函数的返回结果
3. 高阶函数的意义
- 抽象可以帮助我们屏蔽细节,只需关注与我们的目标
- 高级函数使用来抽象通用问题
常用的高阶函数:
forEach、map、filter、every、some、find/findIndex、reduce、sort .....
//函数作为参数 --------------------------------
//forEach 代码实现
function forEach(array, fn){
for(let i=0;i<array.length;i++){
fn(array[i])
}
}
//测试
let arr = [1,3,4,7,8]
forEach(arr,function(item){
console.log(item) //1,3,4,7,8
})
//filter 代码实现
function filter(array,fn){
let result= []
for(let i=0;i<array.length;i++){
if(fn(array[i])){
result.push(array[i])
}
}
return result
}
//测试
let arr = [1,3,4,7,8]
let arrList = filter(arr,function(item){
return item % 2 === 0
})
console.log(arrList) //[4,8]
//函数作为返回值 --------------------------------
function makeFn(){
let msg = "hello function"
return function(){
console.log(msg)
}
}
//测试代码
// const fn = makeFn();
// fn() //hello function
makeFn()() // hello function
//模拟once函数
function once(fn){
let done = false;
return function(){
if(!done){
done = true;
return fn.apply(this,arguments)
}
}
}
let pay = once(function(money){
console.log(`支付了 ${money} RMB`)
})
pay(5)
pay(5)
pay(5)
pay(5)
//只打印依次 支付了 5 RMB
// 模拟常用高阶函数 map every some
//map
const map=(array,fn)=>{
let result = [];
for(let value of array){
result.push(fn(value))
}
return result
}
//测试
let arr = [1,2,3,4]
// arr = map(arr,function(item){
// return item*item
// })
arr = map(arr,v=>v*v)
console.log(arr)
//every
const every = (array,fn)=>{
let result = []
for(let value of array){
result = fn(value)
if(!result){
break
}
}
return result
}
//测试
// let arr = [11,12,14];
let arr = [9,12,14];
let r = every(arr,v=>v>10)
console.log(r)
// arr [11,12,14 ]true
// arr [9,12,14 ] false
// some
const some = (array,fn) =>{
let result = false;
for(let value of array){
result = fn(value)
if(result){
break
}
}
return result;
}
//测试
// let arr = [1,3,4,5]
let arr = [1,3,7,5]
let r = some(arr,v=>v % 2 ===0)
console.log(r)
// arr = [1,3,4,5] true
// arr = [1,3,7,5] false
三、闭包
闭包:可以在另一个作用域中调用一个函数的内部函数并访问到该函数的作用域中的成员(也就是延长了作用域)
闭包本质:函数在执行的时候会放到一个执行栈上,当函数执行完毕之后会从执行栈上移除,但是堆上的作用域成员因为被外部引用不能被释放,因此内部函数依然可以访问外部函数成员
浏览器环境下闭包断点查看
在浏览器F12中可以观察以下作用域:
- 1. 执行栈:Call stack
- 2. 作用域:Scope
- 3. let,const作用域:Script
- 4. 全局作用域:Global
四、lodash
Lodash是一个一致性、模块化、高性能的 JavaScript 高阶函数实用工具库。是一个意在提高开发者效率,提高JS原生方法性能的JS库。简单的说就是,很多方法lodash已经帮我们写好了。和JQ一样Lodash使用了一个简单的_符号。
五、纯函数
什么是纯函数?执行多次函数的输入和输出始终能保持一致.
它的好处有哪些?
- 可缓存:因为纯函数对相同的输入始终都有相同的输出,所以它的结果可以通过闭包被缓存下来(记忆函数)
- 可测试:纯函数让测试更方便(因为纯函数始终有输入和输出,而单元测试就是在断言这个结果)
- 并行处理(web worker):在多线程环境下并行操作共享的内存数据很可能会出现意外情况. 纯函数不需要访问共享的内存数据,所以在并行环境下可以任意运行纯函数
纯函数的副作用
如果纯函数内部的变量依赖于外部的边量就会导致不纯,又如果在内部写死的话会导致硬编码.
外部变量(副作用)的来源:
全局变量 配置文件 数据库 获取用户的输入(跨站脚本攻击)...
所有外部的交互都可能带来副作用,副作用不可能完全禁止,但要尽可能控制它们在可控范围内发生
(ps:函子就用来解决这个问题)
六.柯里化
柯里化:即调用一个函数只传递部分参数(这部分参数永远不变),然后返回一个新的函数并接收剩余的参数并返回结果
七.组合函数(pointFree编程风格)
组合函数
- 如果一个函数要经过对个函数处理才能得到最终的值,这个时候可以把中间过程的函数合并成一个函数
- 函数就是数据的管道,把多个管道连接起来,让数据穿过形成最终的结果
- 函数组合默认是从右到左执行,每一个函数接收一个参数并且返回相应的结果
八.函子
函子:是一个class类(对象),维护一个值,通过map方法去接收一个处理值的函数,并返回一个新的函子
函子特性:
1.函数式编程的运算不直接操作值,而是由函子完成
2.函子就是一个实现了map契约的对象
3.可以把函子想象成一个盒子,这个盒子里封装了一个值
4.想要处理值,需要给map传递一个处理值的纯函数
5.最终map返回一个包含新值的函子