一 纯函数
概念:相同的输入永远得到相同的输出,而且没有任何可观察的副作用;纯函数类似数学中的函数,描述输入和输出直接的映射关系;y=f(x);<br /> 数组操作方法中的纯函数slice和非纯函数splice的举例
let arr = [1,2,3,4,5];// 纯函数console.log(arr.slice(0,3))console.log(arr.slice(0,3))console.log(arr.slice(0,3))// 不纯函数console.log(arr.splice(0,3))console.log(arr.splice(0,3))console.log(arr.splice(0,3))function sum = (a, b){return a + b;}console.log(sum(1,2))console.log(sum(1,2))console.log(sum(1,2))
函数式编程不会保留计算中间的结果,所以变量是不可变的、无状态的;
可以把函数处理的结果交给下个函数去处理;
二 纯函数的好处
可缓存:因为纯函数对相同的输入总会有相同的输出,所以可以把纯函数的结果缓存起来;
模拟lodash memorize函数
function getArea(r) {console.log(r);return Math.PI*r*r}function memorize(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];}}let getAreaWithMemor = memorize(getArea);console.log(getAreaWithMemor(3));console.log(getAreaWithMemor(3));console.log(getAreaWithMemor(3));
可测试:纯函数让测试更方便<br /> 并行处理: 在多线程环境下并行操作共享的内存数据很可能出现意外情况;纯函数不需要访问共享的内存数据,所以在并行环境可以任意运行纯函数(web worker)
三 副作用
副作用来源:配置文件、数据库、获取用户的输入;副作用让纯函数不纯:纯函数根据相同的输入返回相同输出,如果函数依赖与外部的的状态就无法保证输出相同,就会带来副作用;所有的外部交互都有可能带来副作用,副作用也使得方通用性下降、可重用性降低,同时,副作用会给程序带来安全隐患,给程序带来不确定性,但是,副作用不可能完全禁止,尽可能控制他们在可控范围内发生;
let age = 18;function checkAge(num){retrun age>num}function checkAge(num){let age = 18;return age>num;}
四 柯里化
当一个函数有多个参数的时候先传递一部分参数调用它(这部分参数以后保持不变);然后返回一个新的函数接受剩余的参数,然后返回结果;
使用柯里化解决之前案例硬编码的问题
// function checkAge(age){// let min = 18;// return age>=min;// }// 普通的纯函数// function checkAge(min, age) {// return age>=min;// }// 函数的柯里化// function checkAge(min) {// return function(age){// return age>=min// }// }// let check18 = checkAge(18);// ES6let checkAge = min => age => age >= min;let check18 = checkAge(18);
lodash的柯里化 _.curry(fn)
功能:创建一个函数,该函数接受一个或多个fn的函数;如果func所需要的参数都被提供则执行 fn并返回执行结果,否则继续返回该函数并等待接受剩余的参数;(参数:需要柯里化的参数;返回值:柯里化后的函数)
const _ = require('lodash');function getSum(a, b, c){return a + b + c;}const curried = _.curry(getSum);curried(1,2,3)curried(1)(2,3)curried(1,2)(3)
柯里化案例
// ''.match(/\s+/g);// ''.match(/\d+/g);const _ = require('lodash');const match = _.curry(function(reg, str) {return str.match(reg);});const haveSpace = match(/\s+/g);const haveNum = match(/\d+/g);const filter = _.curry((func, array) => array.filter(func));const filterSpace = filter(haveSpace);console.log(haveSpace('hello World'));console.log(haveNum('123abc'));console.log(filterSpace(['Hello world', 'HelloThere']));console.log(filter(haveSpace, ['Hello world', 'HelloThere']))console.log(filter(haveNum, ['12355a', 'HelloThere']))
模拟curry方法
function getSum(a, b, c){return a + b + c;}function curry (func) {return function curryFn(...args) {// 判断实参和形参的个数if(args.length < func.length) {return function(){return curryFn(...args.concat(Array.from(arguments)));}}return func(...args)}}const curried = curry(getSum);console.log(curried(1,2,3));console.log(curried(1)(2,3));console.log(curried(1,2)(3));
柯里化总结:
柯里化可以让我们给一个函数传递较少的参数得到一个已经记住了某些固定值的函数;这是一种对函数参数的缓存;让函数变的更灵活,让函数的颗粒度更小;可以把多元函数转换成一元函数,可以组合使函数产生强大的功能;
