JavaScript纯函数

70 阅读2分钟

纯函数的维基百科定义:

  • 在程序设计中,若一个函数符合以下条件,那么这个函数被称为纯函数:
  • 此函数在相同的输入值时,需产生相同的输出。
  • 函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关。
  • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。
  • 总结:
    • 确定的输入,一定会产生确定的输出;
    • 函数在执行过程中,不能产生副作用;

副作用

  • 在计算机科学中,也引用了副作用的概念,表示在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响,比如修改了全局变量修改参数或者改变外部的存储
  • 纯函数在执行的过程中就是不能产生这样的副作用

纯函数的案例

  • slice:纯函数
    • slice截取数组时不会对原数组进行任何操作,而是生成一个新的数组;
    • slice可以接受两个参数,第一个参数指定要提取的起始位置,第二个参数指定要提取的结束位置。
Array.prototype.slice = function(start, end) {
  const len = this.length;
  let sliceArr = [];
  let begin;

  // 处理start参数
  if (start === undefined) {
    begin = 0;
  } else if (start < 0) {
    begin = Math.max(len + start, 0);
  } else {
    begin = Math.min(start, len);
  }

  // 处理end参数
  let stop;
  if (end === undefined) {
    stop = len;
  } else if (end < 0) {
    stop = Math.max(len + end, 0);
  } else {
    stop = Math.min(end, len);
  }

  // 开始复制元素
  for (let i = begin; i < stop; i++) {
    sliceArr.push(this[i]);
  }

  return sliceArr;
};

  • splice:
    • splice截取数组, 会返回一个新的数组, 也会对原数组进行修改;
    • splice方法是在数组中删除、添加或替换元素的通用方法。它可以接受多个参数,第一个参数指定要进行操作的起始位置,第二个参数指定要删除的元素个数,第三个及后面的参数指定要添加到数组中的新元素。
Array.prototype.splice = function(start, deleteCount, ...items) {
  const len = this.length;
  let deleteArr = [];
  let addArr = [...items];
  let end;

  // 处理start参数
  if (start < 0) {
    start = Math.max(len + start, 0);
  } else {
    start = Math.min(start, len);
  }

  // 处理deleteCount参数
  if (deleteCount < 0) {
    deleteCount = 0;
  } else if (deleteCount > len - start) {
    deleteCount = len - start;
  }

  // 处理addArr参数
  if (start >= len) {
    addArr = [];
  } else if (start + deleteCount > len) {
    end = start + deleteCount;
    addArr = addArr.slice(0, len - end); 
  }

  // 备份删除的元素
  for (let i = start; i < start + deleteCount; i++) {
    deleteArr.push(this[i]);
  }

  // 移动元素位置
  const count = end - start;
  if (addArr.length !== count) {
    if (addArr.length < count) {
      // 后移
      for (let i = len - 1; i >= end; i--) {
        this[i + addArr.length - count] = this[i];
      }
    } else {
      // 前移
      for (let i = end; i < len; i++) {
        this[i + addArr.length - count] = this[i];
      }
    }
  }

  // 插入新元素
  for (let i = 0; i < addArr.length; i++) {
    this[start + i] = addArr[i];
  }

  // 更新数组长度
  this.length = len - deleteCount + addArr.length;

  return deleteArr;
};

纯函数的作用和优势

  • 在写的时候保证了函数的纯度,只是单纯实现自己的业务逻辑即可,不需要关心传入的内容是如何获得的或者依赖其他的外部变量是否已经发生了修改;
  • 用的时候,你确定你的输入内容不会被任意篡改,并且自己确定的输入,一定会有确定的输出;
  • React中就要求我们无论是函数还是class声明一个组件,这个组件都必须像纯函数一样,保护它们的props不被修改:
    • redux中的reducer也被要求是一个纯函数。