函数式编程

119 阅读3分钟

高阶函数

所谓高阶函数,就是函数中可以传入另一个函数作为参数的函数。

函数作为参数

const arr = [1,2,3,4,5,6]

//自定义forEach方法
function forEach(array,fn) {
  for (let index = 0; index < array.length; index++) {
    fn(array[index])  
  }
}
forEach(arr,(item)=>{
  console.log(item);
})

//自定义filter方法
function filter(array,fn) {
  let data = []
  for (let index = 0; index < array.length; index++) {
    if(fn(array[index])){
      data.push(array[index])
    }
  }
  return data
}
const data = filter(arr,(item)=>{
  return item % 2 === 0
})
console.log(data);

函数作为返回值

function makeFn() {
	const msg = '函数作为返回值'
	return function () {
		console.log(msg);
	}
}
const fn = makeFn()
fn()
// makeFn()()

function once(fn) {
	let flag = false
	return function () {
		if(!flag){
			flag = true
			return fn.apply(this,arguments)
		}
	}
}

const oncePay = once(function (money) {
	console.log(`支付金额:${money}`);
})
oncePay(6)
oncePay(7)

模拟常用高阶函数:map,every,some

const arr = [1, 2, 3, 4, 5, 6];

const map = (arr, fn) => {
	let data = [];
	for (const item of arr) {
		data.push(fn(item));
	}
	return data;
};
const mapData = map(arr, item => {
	return item * 2;
});
console.log(mapData);

const every = (arr, fn) => {
	let flag = true;
	for (const item of arr) {
		flag = fn(item);
		if (!flag) {
			break;
		}
	}
	return flag;
};
const everyData = every(arr, item => {
	return item > 1;
});
console.log(everyData);

const some = (arr, fn) => {
	let flag = false;
	for (const item of arr) {
		flag = fn(item);
		if (flag) {
			break;
		}
	}
	return flag;
};
const someData = some(arr, item => {
	return item > 5;
});
console.log(someData);

副作用

  • 纯函数:对于相同的输入永远会得到相同的输出,而且没有任何可观察的副作用
// 不纯的
let mini = 18
function checkAge (age) {
  return age >= mini
}

// 纯的(有硬编码,后续可以通过柯里化解决)
function checkAge (age) {
  let mini = 18
  return age >= mini
}

副作用让一个函数变的不纯(如上例),纯函数的根据相同的输入返回相同的输出,如果函数依赖于外部的状态就无法保证输出相同,就会带来副作用。

副作用来源:

  • 配置文件
  • 数据库
  • 获取用户的输入
  • …… 所有的外部交互都有可能带来副作用,副作用也使得方法通用性下降不适合扩展和可重用性,同时副作用会给程序中带来安全隐患给程序带来不确定性,但是副作用不可能完全禁止,尽可能控制它们在可控范围内发生。

柯里化(Currying):

  • 使用柯里化解决上一个案例中硬编码的问题
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 checkAge = min => (age => age >= min)

let checkAge18 = checkAge(18)
let checkAge20 = checkAge(20)
checkAge18(24)
checkAge18(20)
  • 当一个函数有多个参数的时候先传递一部分参数调用它(这部分参数以后永远不变)
  • 然后返回一个新的函数接收剩余的参数,返回结果

实现柯里化函数

  • 功能:创建一个函数,该函数接收一个或多个 func 的参数,如果 func 所需要的参数都被提供则执行 func 并返回执行的结果。否则继续返回该函数并等待接收剩余的参数。
  • 参数:需要柯里化的函数
  • 返回值:柯里化后的函数
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);
  };
}
  • 柯里化可以让我们给一个函数传递较少的参数得到一个已经记住了某些固定参数的新函数

  • 这是一种对函数参数的'缓存'

  • 让函数变的更灵活,让函数的粒度更小

  • 可以把多元函数转换成一元函数,可以组合使用函数产生强大的功能