JavaScript中的函数式编程

更新日期: 2020-08-22阅读: 1.9k标签: 函数

函数式编程

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

// 数组每个数字加一, 命令式编程
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]

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

// 先拆加一出来
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,我们只需要写一个乘法的方法,然后复用之前的代码就行:

let multiply2 = x => x * 2;

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

事实上我们的加一函数只能加一,也不好复用,它还可以继续拆:

// 先写一个通用加法,他接收第一个加数,返回一个方法
// 返回的这个方法接收第二个加数,第一个加数是上层方法的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;

纯函数可以保证代码的稳定性,因为相同的输入永远会得到相同结果。不纯的函数可能会带来副作用。


函数副作用

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

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

除了我们自己写的函数有副作用外,一些原生api也可能有副作用,我们写代码时应该注意:


我们的目标是尽可能的减少副作用,将函数写为纯函数,下面这个不纯的函数使用了new Date,每次运行结果不一样,是不纯的:


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

// 依赖注入
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. 运算结果通过返回值返回给外部

可变性和不可变性

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

下面是一个可变的例子:


如果我们一定要修改这个参数,我们应该将这个参数进行深拷贝后再操作,这样就不会修改参数了:


作者:蒋鹏飞
链接:https://juejin.im/post/6844904061818142728
来源:掘金

链接: https://fly63.com/article/detial/9598

JavaScript 函数式编程

我理解的 JavaScript 函数式编程,都认为属于函数式编程的范畴,只要他们是以函数作为主要载体的。

Js函数式编程,给你的代码增加一点点函数式编程的特性

给你的代码增加一点点函数式编程的特性,最近我对函数式编程非常感兴趣。这个概念让我着迷:应用数学来增强抽象性和强制纯粹性,以避免副作用,并实现代码的良好可复用性。同时,函数式编程非常复杂。

让我们来创建一个JavaScript Wait函数

Async/await以及它底层promises的应用正在猛烈地冲击着JS的世界。在大多数客户端和JS服务端平台的支持下,回调编程已经成为过去的事情。当然,基于回调的编程很丑陋的。

JavaScript函数创建的细节

如果你曾经了解或编写过JavaScript,你可能已经注意到定义函数的方法有两种。即便是对编程语言有更多经验的人也很难理解这些差异。在这篇博客的第一部分,我们将深入探讨函数声明和函数表达式之间的差异。

编写小而美函数的艺术

随着软件应用的复杂度不断上升,为了确保应用稳定且易拓展,代码质量就变的越来越重要。不幸的是,包括我在内的几乎每个开发者在职业生涯中都会面对质量很差的代码。这些代码通常有以下特征:

javascript回调函数的理解和使用方法(callback)

在js开发中,程序代码是从上而下一条线执行的,但有时候我们需要等待一个操作结束后,再进行下一步操作,这个时候就需要用到回调函数。 在js中,函数也是对象,确切地说:函数是用Function()构造函数创建的Function对象。

js调用函数的几种方法_ES5/ES6的函数调用方式

这篇文章主要介绍ES5中函数的4种调用,在ES5中函数内容的this指向和调用方法有关。以及ES6中函数的调用,使用箭头函数,其中箭头函数的this是和定义时有关和调用无关。

JavaScript中函数的三种定义方法

函数的三种定义方法分别是:函数定义语句、函数直接量表达式和Function()构造函数的方法,下面依次介绍这几种方法具体怎么实现,在实际编程中,Function()构造函数很少用到,前两中定义方法使用比较普遍。

js在excel的编写_excel支持使用JavaScript自定义函数编写

微软 称excel就实现面向开发者的功能,也就是说我们不仅可以全新定义的公式,还可以重新定义excel的内置函数,现在Excel自定义函数增加了使用 JavaScript 编写的支持,下面就简单介绍下如何使用js来编写excel自定义函数。

js中的立即执行函数的写法,立即执行函数作用是什么?

这篇文章主要讲解:js立即执行函数是什么?js使用立即执行函数有什么作用呢?js立即执行函数的写法有哪些?

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!