第六节:纯函数、柯里化、组合函数剖析
一. 纯函数详解
1. 什么是纯函数?
(1). 确定的输入,一定会产生确定的输出;
(2). 函数在执行过程中,不能产生副作用。
2. 什么是副作用?
表示在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响,比如修改了全局变量,修改参数或者改变外部的存储;
3. 纯函数案例
(1). 两个数组操作方法
A. slice:slice截取数组时不会对原数组进行任何操作,而是生成一个新的数组; 【是纯函数!】
B. splice:splice截取数组, 会返回一个新的数组, 也会对原数组进行修改; 【不是纯函数!】
代码分享:
{ let name1 = ["a", "b", "c", "d", "e"]; let name2 = ["a", "b", "c", "d", "e"]; //slice传入两个参数,start和end (不含end位置的数据) console.log("-----------1.slice-------------------"); let newName1 = name1.slice(0, 3); console.log(newName1); //[ 'a', 'b', 'c' ] console.log(name1); //[ 'a', 'b', 'c', 'd', 'e' ] //splice, 传入1个参数,start开始位置,直接截取到最后 console.log("------------2.splice-------------------"); let newName2 = name2.splice(2); console.log(newName2); //[ 'c', 'd', 'e' ] console.log(name2); //[ 'a', 'b' ] }
(2). 其它例子
代码分享:
{ //一. foo函数是否是一个纯函数? // (1).相同的输入一定产生相同的输出 // (2).在执行的过程中不会产生任何的副作用 function foo(num1, num2) { return num1 * 2 + num2 * num2; } //二. bar不是一个纯函数, 因为它修改了外界的变量 var name = "abc"; function bar() { console.log("bar其他的代码执行"); name = "cba"; } bar(); console.log(name); //三. baz也不是一个纯函数, 因为我们修改了传入的参数 function baz(info) { info.age = 100; } //四. test是否是一个纯函数? 【答案:是】 function test(info) { return { ...info, age: 100, }; } }
二. 柯里化详解
1. 什么是柯里化?
2. 柯里化的使用
(1).柯里化的结构和过程
{ console.log("----------------1. 柯里化的结构和过程---------------------"); // 1.1 未柯里化 function add1(x, y, z) { return x + y + z; } console.log(add1(1, 2, 3)); //1.2 实现柯里化 function add2(x) { return function (y) { return function (z) { return x + y + z; }; }; } console.log(add2(1)(2)(3)); //1.3简化柯里化 let add3 = x => y => z => { return x + y + z; }; let add4 = x => y => z => x + y + z; console.log(add3(1)(2)(3)); console.log(add4(1)(2)(3)); }
(2). 柯里化单一职责原则
{ console.log("----------------2. 柯里化--单一职责原则---------------------"); function add(x, y, z) { x = x + 2; y = y * 2; z = z * z; return x + y + z; } console.log(add(10, 20, 30)); function sum(x) { x = x + 2; return function (y) { y = y * 2; return function (z) { z = z * z; return x + y + z; }; }; } console.log(sum(10)(20)(30)); }
(3). 柯里化-逻辑复用
{ console.log("----------------3. 柯里化--逻辑复用---------------------"); //3.1 假如在程序中,我们经常需要把5和另外一个数字进行相加 function makeAdder(count) { count = count * count; return function (num) { return count + num; }; } var adder5 = makeAdder(5); console.log(adder5(10)); console.log(adder5(20)); //3.2 日志场景 var log = date => type => message => { console.log( `[${date.getHours()}:${date.getMinutes()}][${type}]: [${message}]` ); }; // 如果我现在打印的都是当前时间 var nowLog = log(new Date()); nowLog("DEBUG")("查找到轮播图的bug"); nowLog("FETURE")("新增了添加用户的功能"); var nowAndDebugLog = log(new Date())("DEBUG"); nowAndDebugLog("查找到轮播图的bug"); nowAndDebugLog("查找到轮播图的bug"); nowAndDebugLog("查找到轮播图的bug"); nowAndDebugLog("查找到轮播图的bug"); var nowAndFetureLog = log(new Date())("FETURE"); }
3. 手写柯里化函数
(1). 接收的参数是1个函数,返回值也是1个函数
(2). 当传入的参数 大于/等于 需要的参数时,立即执行函数
(3). 当传入的参数 小于 需要的参数时,返回一个新函数,继续来接收参数
(4). 这个新函数接收到参数后,递归来调用,并且合并参数
代码分享:
function ypfCurrying(fn) { function curried(...args) { // 判断当前已经接收的参数的个数, 可以参数本身需要接受的参数是否已经一致了 // 1.当已经传入的参数 大于等于 需要的参数时, 就执行函数 if (args.length >= fn.length) { // fn(...args) // fn.call(this, ...args) return fn.apply(this, args); } else { // 2. 没有达到个数时, 需要返回一个新的函数, 继续来接收的参数 function curried2(...args2) { // 接收到参数后, 需要递归调用curried来检查函数的个数是否达到 return curried.apply(this, args.concat(args2)); } return curried2; } } return curried; } function myAdd(x, y, z) { return x + y + z; } // 调用测试 let myCurryAdd = ypfCurrying(myAdd); console.log(myCurryAdd(10, 20, 30)); console.log(myCurryAdd(10, 20)(30)); console.log(myCurryAdd(10)(20)(30));
三. 组合函数详解
1. 什么是组合函数?
组合(Compose)函数是在JavaScript开发过程中一种对函数的使用技巧、模式: 比如我们现在需要对某一个数据进行函数的调用,执行两个函数fn1和fn2,这两个函数是依次执行的;
那么如果每次我们都需要进行两个函数的调用,操作上就会显得重复;那么是否可以将这两个函数组合起来,自动依次调用呢? 这个过程就是对函数的组合,我们称之为 组合函数(Compose Function)。
2. 组合函数案例
案例: 对2倍函数 和 平方函数进行组合
(1). 先2倍→ 然后平方
(2). 先平方 → 然后2倍
代码分享:
{ function myDouble(num) { return num * 2; } function mySquare(num) { return num * num; } //不组合的调用 console.log(myDouble(mySquare(10))); //先平方 → 然后2倍 console.log(mySquare(myDouble(10))); //先2倍→ 然后平方 //构建组合函数 function myCompose(fnFirst, fnLast) { return function (x) { return fnLast(fnFirst(x)); }; } let myFn1 = myCompose(myDouble, mySquare); let myFn2 = myCompose(mySquare, myDouble); console.log(myFn1(10)); //先2倍→ 然后平方 console.log(myFn2(10)); //先平方 → 然后2倍 }
3. 手写组合函数
代码分享:
function ypfCompose(...fns) { var length = fns.length; for (var i = 0; i < length; i++) { if (typeof fns[i] !== "function") { throw new TypeError("Expected arguments are functions"); } } // 需要重点理解一下,这是难点 function compose(...args) { var index = 0; var result = length ? fns[index].apply(this, args) : args; while (++index < length) { result = fns[index].call(this, result); } return result; } return compose; } function double(m) { return m * 2; } function square(n) { return n ** 2; } var newFn = ypfCompose(double, square); console.log(newFn(10));
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。