• 第六节:纯函数、柯里化、组合函数剖析


    一. 纯函数详解

    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));
    View Code

    三. 组合函数详解

    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));
    View Code

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    (并查集)小希的迷宫 --HDU -- 1272
    (并查集)Connections in Galaxy War -- zoj --3261 还没写
    (并查集)A Bug's Life -- POJ -- 2492
    LINQ 图解 LINQ学习第三篇 [转]
    C# 4.0 新特性-dynamic 【转】
    C#编程规范
    C#中关于DateTime的最大值和最小值
    .NET,你忘记了么?(八)—— 从dynamic到特性误用 [转]
    C#中dynamic的正确用法【转】
    ASP.NET MVC 中将FormCollection与实体间转换方法【转】
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/15916640.html
Copyright © 2020-2023  润新知