欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【JS】纯函数

程序员文章站 2024-02-08 14:42:40
...

纯函数定义

相同的输入总会得到相同的输出,并且不会产生副作用(额外的影响)的函数,就是纯函数。

相同的输入总会得到相同的输出

  • 非纯函数:
var a = 10

function add(b) {
    a += b
  return a
}

add(1) // 11
add(1) // 12
add(1) // 13
add(1) // 14
  • 纯函数:
function add(a, b) {
  return a + b
}

add(1, 2)   // 3
add(1, 2)   // 3
add(1, 2)   // 3
add(1, 2)   // 3

除了传入的参数之外,不依赖任何外界的信息与状态

  • 依赖外界信息的非纯函数:
// 外界的 name 变量
var name = 'Jake';

function sayHello() {
  // 该函数依赖外界变量,导致变量改变时,返回的值不一致
  return 'Hello, ' + name;
}

sayHello();  // Hello, Jake

// 当我们有其他需求需要改变name的值
name = 'Tom';
sayHello(); // Hello, Tom
  • 把外界变量当成参数来传递用:
function sayHello(name) {
  return 'Hello, ' + name;
}
sayHello();  // Hello,
sayHello();  // Hello,
sayHello('Jake');  // Hello, Jake
sayHello('Jake');  // Hello, Jake
sayHello('Tom'); // Hello, Tom
sayHello('Tom'); // Hello, Tom

无副作用不产生额外的影响

  • 示例:获取函数的最后一项
function getLast(arr) {
  return arr[arr.length];
}

function getLast_(arr) {
  return arr.pop();
}

var source = [1, 2, 3, 4];

var last = getLast(source);  // 返回结果4  原数组不变
var last_ = getLast_(source); // 返回结果4 原数据最后一项被删除,产生了副作用
  • 示例:是否改变元素组
var source = [1, 2, 3, 4, 5];

source.slice(1, 3);  // 纯函数 返回[2, 3] source不变
source.splice(1, 3); // 不纯的 返回[2, 3, 4] source被改变

source.pop(); // 不纯的
source.push(6); // 不纯的
source.shift(); // 不纯的
source.unshift(1); // 不纯的
source.reverse(); // 不纯的

// 我也不能短时间知道现在source被改变成了什么样子,干脆重新约定一下
source = [1, 2, 3, 4, 5];

source.concat([6, 7]);  // 纯函数 返回[1, 2, 3, 4, 5, 6, 7] source不变
source.join('-'); // 纯函数 返回1-2-3-4-5 source不变
  • 纯函数必须遵守以下一些约束:
1. 不得改写参数数据
2. 不能调用Date.now()或者Math.random()等不纯的方法
3. 不能调用系统 I/OAPI
4. 不能调用网络请求、输入和输出设备
5. redux 的 reducer 函数必须是一个纯函数

纯函数优点

可复用性

  • 无论在封装一个函数,一个库还是一个组件时,都期望一次封装,多处使用。
  • 而纯函数刚好具备这样的特性。

纯函数仅依赖于传入的参数,只有提供它需要的参数,就可随意将这个函数移植到别的代码中。

如果是非纯函数,你可以只需要其中的某一项功能,但是却不得不把整个函数都拿过来用

  • 示例:获取url参数对应的值
function getParams(url, param) {
  if (!/\?/.test(url)) {
    return null;
  }

  var search = url.split('?')[1];
  var array = search.split('&');

  for(var i = 0; i < array.length; i++) {
    var tmp = array[i].split('=');
    if (tmp[0] === param) {
      return decodeURIComponent(tmp[1]);
    }
  }

  return null;
}

var url = 'https://www.baidu.com/s?tn=baidu&wd=javascript&rsv_sug=1';
getParams(url, 'wd'); // javascript

可缓存性

因为相同的输入总能得到相同的输出,因此,如果函数内部计算非常复杂,当我们发现输入与上一次相同时,可以直接返回结果而不用经过内部的计算。这是一种性能优化的策略。

  • 获取当天的数据
// 传入日期,获取当天的数据
function process(date) {
  var result = '';

  // 假设这中间经历了复杂的处理过程

  return result;
}

function withProcess(base) {
  var cache = {}

  return function() {
    var date = arguments[0];
    if (cache[date]) {
      return cache[date];
    }
    cache[date] = base.apply(base, arguments);
    return cache[date]
  }
}

var _process = withProcess(process);

// 经过上面一句代码处理之后,我们就可以使用_process来获取我们想要的数据,
// 如果数据存在,会返回缓存中的数据,
// 如果不存在,则会调用process方法重新获取。
_process('2017-06-03');
_process('2017-06-04');
_process('2017-06-05');
  • 扩展:arguments 是一个对应于传递给函数的参数的类数组对象。
function func1(a, b, c) {
  console.log(arguments[0]);
  // expected output: 1

  console.log(arguments[1]);
  // expected output: 2

  console.log(arguments[2]);
  // expected output: 3
}

func1(1, 2, 3);