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

一. 纯函数详解

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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2022-02-20 21:31  Yaopengfei  阅读(133)  评论(1编辑  收藏  举报