JavaScript 函数式编程
前置知识
- JavaScript 基础
- JavaScript 面向对象
函数式编程含义
函数式编程是一种强调以函数使用为主的软件开发风格 ,也是一种范式。
某些函数式编程语言 Haskell、lisp、Scheme 等。
JavaScript 中函数式编程
数学中函数
f(x) = y;
JavaScript 中的函数
let factor = 3;
let totalNum = num=>factor*num;
console.log( totalNum(3) );
对比两种函数,基于数学函数 来修复 JavaScript 函数
let totalNum = (num,factor)=>factor*num;
console.log( totalNum(3,3) );
JavaScript 是多范式编程语言,但是函数作为一等公民,函数式编程具有天然优势。
函数式编程中涉及到的概念
纯函数
函数式编程基于纯函数
纯函数是对给定的输入返还相同输出的函数;例如
let double = value=>value*2;
纯函数意义
纯函数可以产生可测试的代码
1、不依赖外部环境计算,不会产生副作用,提高函数的复用性。
test('double(2) 等于 4', () => {
expect(double(2)).toBe(4);
})
2、可读性更强 ,JavaScript 函数不管是否是纯函数 都会有一个语义化的名称,更便于阅读。
3、可以组装成复杂任务的可能性。符合模块化概念及单一职责原则。
高阶函数
高阶函数定义
高阶函数:以函数作为输入或者输出的函数被称为高阶函数(Higher-Order Function)。
高阶函数的抽象
一般高阶函数用于抽象通用问题,简而言之,高阶函数就是定义抽象。
命令式循环(注重 如何 做,注重过程);
let arr = [1,2,3];
for(let i=0;i<arr.length;i++){
console.log(arr[i]);
}
通过高阶函数抽象过程,声明式编程(注重做 什么,注重结果);
const forEach = function(arr,fn){
for(let i=0;i<arr.length;i++){
fn(arr[i]);
}
}
let arr = [1,2,3];
forEach(arr,(item)=>{
console.log(item);
})
上面通过高阶函数 forEach 来抽象循环 如何 做的逻辑,直接关注 做 什么。
高阶函数的缓存特性
主要是利用函数的闭包
once 高阶函数
const once = (fn)=>{
let done = false;
return function(){
if(!done){
fn.apply(this,fn);
}else{
console.log("this fn is already execute");
}
done = true;
}
}
function test(){
console.log("test...");
}
let myfn = once(test);
myfn();
myfn();
柯里化
什么是柯里化?
柯里化是把一个多参数函数转化成一个嵌套的一元函数的过程;
如下二元函数
let fn = (x,y)=>x+y;
柯里化函数
const curry = function(fn){
return function(x){
return function(y){
return fn(x,y);
}
}
}
let myfn = curry(fn);
console.log( myfn(1)(2) );
多参数函数柯里化
// 多参数柯里化;
const curry = function(fn){
return function curriedFn(...args){
if(args.length<fn.length){
return function(){
return curriedFn(...args.concat([...arguments]));
}
}
return fn(...args);
}
}
const fn = (x,y,z,a)=>x+y+z+a;
const myfn = curry(fn);
// console.log(myfn(1)(2));
console.log(myfn(1)(2)(3)(1));
柯里化意义
- 让纯函数更 纯,每次接受一个参数,松散解耦
- 某些语言及特定环境下只能接受一个参数
- 惰性执行
组合(composition)和管道(pipe)
组合(composition)
组合函数:无需创建新的函数,通过基础函数解决眼前问题。
组合高阶函数 map 及 filter;
- map 高阶函数
- 组合 map 及 filter 来使用
compose 组合
可以封装组合函数来实现函数执行
2 个函数组合
function afn(a){
return a*2;
}
function bfn(b){
return b*3;
}
const compose = (a,b)=>c=>a(b(c));
let myfn = compose(afn,bfn);
console.log( myfn(2));
多函数组合
const compose = (...fns)=>val=>fns.reverse().reduce((acc,fn)=>fn(acc),val);
管道(pipe)
compose 执行是从右到左,pipe 是从左至右的执行。函数如下:
const pipe = (...fns)=>val=>fns.reduce((acc,fn)=>fn(acc),val);
管道、组合 取舍 :管道及组合最大区别在于执行顺序的不同,数据流向不同,达到目的是类似的。所以无优 劣之分,保持团队风格统一就好了。
组合及管道的意义 把很多小函数组合起来完成更复杂的逻辑。
Pointfree 编程风格
概念:不适用处理的值,只合成运算过程,也就是无值风格。
获取所有的句号
const sentenceNum = str=>str.match(/。/g);
let str = "大家好,我是中国人。我爱中国。我们同住地球村。";
console.log(sentenceNum(str));
统计长度
const countFn = arr=>arr.length;
判断奇偶
const oddOrEven = num=>num%2===0?"偶数":"奇数";
pointfree 风格组合函数使用:找到句号统计长度最后判断奇偶数
let str = "大家好,我是中国人。我爱中国。我们同住地球村。";
const myfn = compose(oddOrEven,countFn,sentenceNum);
console.log(myfn(str));
JavaScript 函数式编程库
- lodash.js 、ramda.js 、Underscore.js
- 通过 ramda.js 实现数据筛选功能;
函数式编程在 redux 中应用
Redux 整体是通过函数式 编程思维实现的;
createStore 简单实现
const createStore = (reducer, initialState) => {
const store = {};
let currentState = initialState;
const listeners = [];
const getState = () => currentState;
const subscribe = (listener) => {
listeners.push(listener);
};
const dispatch = (action) => {
// store.state = deepFreeze(reducer(store.state, action) );
currentState = reducer(currentState, action);
listeners.forEach(listener => listener());
};
return {
getState,
subscribe,
dispatch
};
};
export {createStore};
状态管理调用
import {createStore} from "../MyRedux.js";
// import { createStore } from 'redux';
function Counter(state={count:0},action) {
switch (action.type) {
case "ADD_COUNT":
return {count:state.count + 1}
break;
case "REDUCE_COUNT":
return {count:state.count - 1}
break;
default:
return state;
break;
}
}
var store = createStore(Counter);
btn.onclick = function(){
// console.log(store);
store.dispatch({type:"ADD_COUNT"});
}
store.subscribe(() => {
// console.log(store)
renderDom()
})
function renderDom(){
let mydiv = document.querySelector(".countDiv");
if(typeof store.getState() !== "undefined"){
mydiv.innerHTML = store.getState().count;
}
}
函子
容器函子 Functor
函子是函数式编程里面最重要的数据类型,也是基本的运算单位和功能单位。
定义自己的Functor。
class Container{
constructor(value){
this._value = value;
}
static of(val){
return new Container(val);
}
}
let res = Container.of(1);
console.log(res);
通过 map 修改 Functor 值
class Container{
constructor(value){
this._value = value;
}
static of(val){
return new Container(val);
}
map(fn){
return Container.of(fn(this._value));
}
}
let res = Container.of(1);
// console.log(res);
let reslut = res.map(item=>{
// console.log(item);
return item+3
}).map(item=>{
return "最终结果是" + item;
})
MeBe 函子
处理空值的函子
class Mabe{
constructor(val){
this._value = val;
}
static of(val){
return new Mabe(val);
}
isNoThing(){
return (this._value===null || this._value=== undefined);
}
map(fn){
return this.isNoThing()?Mabe.of(null):Mabe.of(fn(this._value));
}
}
Either 函子
class Either extends Functor{
constructor(left,rigth){
super();
this.left = left;
this.right = rigth;
}
map(f){
return this.right?Either.of(this.left, f(this.right)) :
Either.of(f(this.left), this.right);
}
static of(left, right){
return new Either(left, right);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论