深入浅出JS—06 纯函数、函数柯里化、组合函数

函数式编程是一种编程范式,它不是JavaScript中特有的,只能说JavaScript遵循函数式编程范式
本文主要讲解函数式编程中:纯函数、函数柯里化以及组合函数的概念和用法



💊 纯函数 (无副作用)

1 纯函数定义

  • 确定的输入参数,一定会产生确定的输出(输出不依赖外部变量,因为外部变量可能会改变)
  • 不产生副作用:修改外部变量,修改传入参数,修改外部存储、触发事件

2 纯函数的特点

不依赖,不打扰

/* 判断以下函数是否为纯函数*/

let age = 18;
function bar(){
  return age; 
}
// bar不是纯函数,输出依赖外部变量


const obj = {name: 'xs', age: 8};
let n = 10;
function foo(obj) {
	obj.age = 18;
	n = 15;
} 
// foo不是纯函数,修改了参数,修改了外部变量


function demo(key, str) {
  localStorage.setTitem(key, str);
}
// demo不是纯函数,修改了外部存储

// 数组方法slice是纯函数,而splice不是纯函数,会修改原数组
 let nums = [11, 22, 433, 21];
 let newNums2 = nums.splice(2);
 console.log(newNums2); // [ 433, 21 ]
 console.log(nums); // [ 11, 22 ]

应用
react中规定组件必须是个纯函数,不可以修改传入的参数props的大小,因为组件会被大量复用,若一处产生了副作用,势必会为其他组件的使用带来隐患。
注意
在编程中尽可能写纯函数,不是绝对的,比如set函数就是为了改变一些值


⏳ 函数柯里化 (一步一步传参)

柯里化currying也是函数式编程中的概念,不仅仅属于JS

1 柯里化定义

  • 柯里化是一个过程:现有一个函数,它需要接收多个参数,通过柯里化,将其变成另一个函数,它只接收一部分参数,返回一个函数去处理剩余参数
  • 多个参数拆分成多次函数调用的过程就是柯里化

2 柯里化举例

// 现有一个函数add,它需要接收多个参数
function add(x, y, z) {
	return x + y + z;
}

// 将add柯里化为newAdd
// newAdd只接收一部分参数,返回一个函数去处理剩余参数
function newAdd(x) {
	return function(y) {
		return function(z) {
			return x + y + z;
		}
	}
}
// 简写形式如下
const newAdd = x => y => z => x + y + z;

// 正常函数调用 VS 柯里化后的函数调用
add(1, 2, 3);
newAdd(1)(2)(3);
// 多个参数拆分成多次函数调用的过程就是柯里化

3 柯里化作用

单一职责原则

每一层函数只处理一个传入的参数

function add(x, y, z) {
  x = x + 2;
  y = y * 2;
  z = z * z;
  return x + y + z;
}

function newAdd(x) {
  // 第一层函数只处理x
  x = x + 2;

  return function(y) {
    // 第一层函数只处理y
    y = y * 2;
    
    return function(z) {
      // 第一层函数只处理z
      z = z * z;

      return x + y + z;
    }
  }
}

逻辑复用原则

(制造工具函数)

案例1:制造加法工具函数

const add = (x, y) => x + y;
const makeAdder = x => y => x + y; // add函数柯里化

// 假设在程序中经常需要将5与其他数字相加
const add5 = makeAdder(5);
add5(10); //15

案例2:打印日志(时间,类型,信息)函数被多次调用,部分参数多次重复

const log = (date, type, message) => {
  console.log(`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]`)
}

// 如果前两个参数都一样,每次都重写好麻烦,能不能复用一下
log(new Date(), 'DEBUG', '查找轮播图bug')
log(new Date(), 'DEBUG', '查找数据bug')
log(new Date(), 'DEBUG', '查找菜单bug')

// 将log函数柯里化
const makeLog = date => type => message => {
	console.log(`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]`)
}
// 制造函数
const debugLog = makeLog(new Date() , 'DEBUG');
debugLog('查找轮播图bug')
debugLog('查找数据bug')
debugLog('查找菜单bug')

4 柯里化加工函数

构造一个myCurrying函数,输入为一个函数,输出柯里化后的函数

function myCurrying(fn) {
	function curriedFn(...args1) {
		if (args1.length >= fn.length){// 传入参数足够
			return fn.call(this, ...args1); 
		} else{ // 传入参数不够,需要再返回个函数接收其他参数
			return function(...args2){
				return curriedFn.call(this, ...args1, ...args2);
			}
		}	
	}
	return curriedFn;
}


const curriedFn = myCurrying(fn);
fn(...args);
curriedFn(...args1)(...args2)(...rest);
curriedFn.call('abc', ...args1)(...args2)(...rest); // 由于函数调用时可能绑定this,所以在函数内部也要用call方法来传递this

Tips

  • fn.length可以取到函数fn参数的数目

🧅 组合函数 (像洋葱层层相套)

组合函数是个应用的小技巧,将一个函数的结果作为另一个函数的入参,像洋葱一样调用

1 组合函数举例

// 组合函数(洋葱
const g = n => n * 2;
const f = n => n ** 2;

function composeFn(f, g) {
  return function(count) {
    return g(f(count))
  }
}

const fg = composeFn(f, g);
fg(3) //36

2 组合函数加工器的实现

function composeFn(...fns) {
	// 若传入参数不是函数类型,报错
	fns.forEach(fn => {
		if(typeof fn !== 'function)
			throw new TypeError('参数必须都为函数');
	})
	
	// 返回组合函数
	return function(...args){
	// JS中函数的入参可以有多个,但是输出只有一个
	let ans = fns.length ? fns[0].call(this, ...args) : args;
    for (let i = 1; i < fns.length; i++) {
      ans = fns[i](ans);
    }
    return ans;
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值