JavaScript中的函数式编程

函数式编程

函数式编程是一种编程范式,是一种构建计算机程序结构和元素的风格,它把计算看做是对数学函数的评估,避免了状态的变化和数据的可变,与函数式编程相对的是命令式编程。咱们有这样一个需求,给数组的每一个数字加一:javascript

// 数组每一个数字加一, 命令式编程
let arr = [1, 2, 3, 4];
let newArr = [];
for(let i = 0; i < arr.length; i++){
  newArr.push(arr[i] + 1);
}

console.log(newArr); // [2, 3, 4, 5]

这段代码结果没有问题,可是无法重用。咱们换一个思惟,这里面包含的操做其实就两个,一个是遍历数组,一个是成员加一。咱们把这两个方法拆出来:java

// 先拆加一出来
let add1 = x => x +1;

// 而后拆遍历方法出来,经过遍历返回一个操做后的新数组
// fn是咱们须要对每一个数组想进行的操做
let createArr = (arr, fn) => {
  const newArr = [];
  for(let i = 0; i < arr.length; i++){
    newArr.push(fn(arr[i]));
  }

  return newArr;
} 

// 用这两个方法来获得咱们指望的结果
const arr = [1, 2, 3, 4];
const newArr = createArr(arr, add1);
console.log(newArr);  // [2, 3, 4, 5], 结果仍然是对的

这样拆分后,若是咱们下次的需求是对数组每一个元素乘以2,咱们只须要写一个乘法的方法,而后复用以前的代码就行:git

let multiply2 = x => x * 2;

// 调用以前的createArr
const arr2 = [1, 2, 3, 4];
const newArr2 = createArr(arr2, multiply2);
console.log(newArr2);  // [2, 4, 6, 8], 结果是对的

事实上咱们的加一函数只能加一,也很差复用,它还能够继续拆:github

// 先写一个通用加法,他接收第一个加数,返回一个方法
// 返回的这个方法接收第二个加数,第一个加数是上层方法的a
// 这样当咱们须要计算1+2是,就是add(1)(2)
let add = (a) => {
  return (b) => {
    return a + b;
  }
}

// 咱们也能够将返回的函数赋给一个变量,这个变量也就变成一个能特定加a的一个方法
let add1 = add(1);

let res = add1(4); 
console.log(res);  // 5

因此函数式编程就是将程序分解为一些更可重用、更可靠且更易于理解的部分,而后将他们组合起来,造成一个更易推理的程序总体。编程

纯函数

纯函数是指一个函数,若是它的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,只依赖于其输入参数。同时函数的运行也不改变任何外部数据,它只经过它的返回值与外部通信。数组

下面这个函数就不是纯函数,由于函数内部须要的discount须要从外部获取:函数式编程

let discount = 0.8;
const calPrice = price => price * discount;
let price = calPrice(200);  // 160

// 当discount变了,calPrice传一样额参数,结果不同,因此不纯
discount = 0.9;
price = calPrice(200);  // 180

要改成纯函数也很简单,将discount做为参数传递进去就好了函数

const calPrice = (price, discount) => price * discount;

纯函数能够保证代码的稳定性,由于相同的输入永远会获得相同结果。不纯的函数可能会带来反作用。spa

函数反作用

函数反作用是指调用函数时除了返回函数值以外,还对主调用函数产生附加的影响,好比修改全局变量或者外部变量,或者修改参数。这可能会带来难以查找的问题并下降代码的可读性。下面的foo就有反作用,当后面有其余地方须要使用a,可能就会拿到一个被污染的值code

let a = 5;
let foo = () => a = a * 10;
foo();
console.log(a); // 50

除了咱们本身写的函数有反作用外,一些原生API也可能有反作用,咱们写代码时应该注意:

image-20200109232215022

咱们的目标是尽量的减小反作用,将函数写为纯函数,下面这个不纯的函数使用了new Date,每次运行结果不同,是不纯的:

image-20200109232541307

要给为纯函数能够将依赖注入进去,所谓依赖注入就是将不纯的部分提取出来做为参数,这样咱们可让反作用代码集中在外部,远离核心代码,保证核心代码的稳定性

// 依赖注入
const foo = (d, log, something) => {
  const dt = d.toISOString();
  return log(`${dt}: ${something}`);
}

const something = 'log content';
const d = new Date();
const log = console.log.bind(console);
foo(d, log, something);

因此减小反作用通常的方法就是:

1. 函数使用参数进行运算,不要修改参数
2. 函数内部不修改外部变量
3. 运算结果经过返回值返回给外部

可变性和不可变性

  • 可变性:指一个变量建立之后能够任意修改
  • 不可变性: 指一个变量被建立后永远不会发生改变,不可变性是函数式编程的核心概念

下面是一个可变的例子:

image-20200109233313733

若是咱们必定要修改这个参数,咱们应该将这个参数进行深拷贝后再操做,这样就不会修改参数了:

image-20200109233515929

文章的最后,感谢你花费宝贵的时间阅读本文,若是本文给了你一点点帮助或者启发,请不要吝啬你的赞和GitHub小星星,你的支持是做者持续创做的动力。

做者博文GitHub项目地址: https://github.com/dennis-jiang/Front-End-Knowledges

相关文章
相关标签/搜索