专栏首页司想君即学即用系列一:纯函数

即学即用系列一:纯函数

最近一直在思考如何通过文章或者培训快速提升团队的编码能力,总结下来其实技术的学习分为两类:一种是系统性的学习,比如学习一门语言,学习一个开发框架,这更需要自己从入门到进阶再到实践一步步系统性的学习,单靠几篇文章或者几次培训,效果并不明显;还有一种是技巧性的学习,比如某些编程实践、设计原则,其实并没有多么复杂,但是不知道就不会用,知道了就会有意识去用,就好比玩微信游戏跳一跳,在某些方块上停留一段时间就会获得加分,不知道的时候根本想不到,知道了以后想不用都难。于是就有了《即学即用》这个系列的文章。

系列第一篇,就从纯函数开始,由于我是前端方向,所以就从JavaScript语言中的纯函数说起。

什么是纯函数

纯函数是函数式编程中非常重要的一个概念,简单来说,就是一个函数的返回结果只依赖于它的参数,并且在执行过程中没有副作用,我们就把这个函数叫做纯函数。

下面我们来划重点:

  1. 函数的返回结果只依赖于它的参数
  2. 函数执行过程中没有副作用

首先来解释第一点:函数的返回结果只依赖于它的参数

const a = 1
const impure = (b)=>a + b
impure(2) // 3

上面代码中,impure函数不是一个纯函数,因为它的返回结果依赖外部变量a,因为a是有可能变化的,所以我们不能保证impure(2)的值永远是3。虽然impure函数的代码没有变化,传入的参数也没有变化,但它的返回值是不可预料的。我们再来改写一下:

const a = 1
const pure = (x, b) => x + b
pure(1,2) //3

现在,pure的返回结果只依赖于它的参数xb,就是说,只要代码不变,pure(1, 2)的返回值永远是3。

这就是纯函数的第一个条件:函数的返回结果只依赖于它的参数

接下来解释第二点:函数执行中没有副作用

副作用是指:在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的可观察的交互。我们再看一个例子:

var values = { a: 1 };

function impureFunction ( items ) {
  var b = 1;

  items.a = items.a * b + 2;

  return items.a;
}

var c = impureFunction( values );
values.a // 3

在上面的代码中,我们改变了参数对象中的一个属性。由于我们定义的函数改变的对象在我们的函数作用域之外,导致这个函数成为“不纯”的函数。

var values = { a: 1 };

function pureFunction ( a ) {
  var b = 1;

  a = a * b + 2;

  return a;
}

var c = pureFunction( values.a );
values.a // 1

上面的代码,我们只计算了作用域内的局部变量,没有任何作用域外部的变量被改变,因此这个函数是“纯函数”。

除了修改外部的变量,一个函数在执行过程中还有很多方式产生外部可观察的变化,比如说调用 DOM API 修改页面,或者你发送了 Ajax 请求,还有调用 window.reload刷新浏览器,甚至是 console.log 往控制台打印数据也是副作用。

纯函数很严格,也就是说你几乎除了计算数据以外什么都不能干,计算的时候还不能依赖除了函数参数以外的数据。

我们再来用JavaScript中常用的两个方法slicesplice来举一个例子:

var array1 = [0,1,2,3,4,5,6];
var array2 = [0,1,2,3,4,5,6];

var spliceArray = array1.splice(0,2);
var sliceArray = array2.slice(0,2);

console.log('array1: ' + array1);
console.log('spliceArray: ' + spliceArray);

console.log('array2: ' + array2);
console.log('sliceArray: ' + sliceArray); 

运行结果:

array1: 2,3,4,5,6
spliceArray: 0,1
array2: 0,1,2,3,4,5,6
sliceArray: 0,1

可以看到,slicesplice的作用是大致相同的,但是splice改变了原数组,而slice却没有,实际开发中,slice这种不改变原数组的方式更安全一些,改变原始数组,是一种副作用

非纯函数带来的副作用

既然我们推荐纯函数,那么肯定是因为非纯函数有缺陷。我们看下面的代码:

function getName(obj){
    return obj.name;
}
function getAge(obj){
  return obj.age;
}
function sayHi(person){
  console.log('I am' + getName(person) + ',and I am' + getAge(person) + 'years old');
}

var Tom = {
  name: 'TOM',
  age: 26
};

sayHi(Tom);

我们说sayHi不熟纯函数,它依赖于getNamegetAge两个函数,如果我不小心改变了其中某个函数的功能,这将使得sayHi这个函数出现错误。当网页变得复杂,且由多人维护的时候,bug调试会变得非常复杂。

使用纯函数的优点

1. 可复用性

纯函数仅依赖于传入的参数,这意味着你可以随意将这个函数移植到别的代码中,只需要提供踏需要的参数即可。如果是非纯函数,有可能你需要一根香蕉,却需要将整个香蕉树搬过去。

2. 可测试性

纯函数非常容易进行单元测试,因为不需要考虑上下文环境,只需要考虑输入和输出。

3. 并行代码

纯函数是健壮的,改变执行次序不会对系统造成影响,因此纯函数的操作可以并行执行。

总结

虽然纯函数有很多优点,但也要避免滥用的情况。函数越纯,对环境依赖越小,往往意味着要传入更多的参数。我们的最终目的是:让你的代码尽可能简单易懂和灵活。这篇文章主要介绍了JavaScript中纯函数的概念,但是在很多其他开发语言中,纯函数的概念是一样通用的,比如笔者正在自学的JAVA,欢迎大家针对各种语言中对纯函数的理解和我一起讨论。

本文分享自微信公众号 - 司想君(sxjsaylife),作者:司想君

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-02-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JavaScript闭包,只学这篇就会了

    昨天发的文章,排版出现了重大失误。让大家的眼睛受累了。今天再发一遍。 这篇文章使用一些简单的代码例子来解释JavaScript闭包的概念,即使新手也可以轻松参透...

    司想君
  • JavaScript经典面试题之for循环click

    经典重现 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title><...

    司想君
  • 详解Object.create(null)

    司想君
  • C语言之函数

      结构化程序设计主张按功能来分析需求,主要原则自顶向下,逐步求精,模块化等。 主张按功能把软件系统逐步细分,每个功能都负责对数据进行一次处理,每个功能接收一些...

    互联网金融打杂
  • Python 函数进阶

    高阶函数是至少满足下面一个条件的函数 # 接受一个或多个函数作为参数 # 输...

    江小白
  • 写 Python 代码不可不知的函数式编程技术

    近来,越来越多人使用函数式编程(functional programming)。因此,很多传统的命令式语言(如 Java 和 Python)开始支持函数式编程技...

    机器之心
  • 19. 再说函数~那些不得不知道的事儿

    前面的课程中,我们已经对函数有了简单的了解 函数的声明、函数的的调用、函数的参数以及返回值等等

    大牧莫邪
  • 详解javascript中的即时函数,内部函数,能重写自身的函数即时函数内部函数返回函数的函数能重写自己的函数小结

    在上篇谈到匿名函数和回调函数的基础上,我们接着介绍javascript中的即时函数,内部函数,返回函数的函数,能重写自身的函数等几种常见的函数类型及使用方法。只...

    desperate633
  • 聊一聊编程中的函数

    给定一个数集A,假设其中的元素为x。现对A中的元素x施加对应法则f,记作f(x),得到另一数集B。假设B中的元素为y。则y与x之间的等量关系可以用y=f(x)表...

    算法与编程之美
  • 理解神经网络的激活函数

    激活函数在神经网络中具有重要的地位,对于常用的函数如sigmoid,tanh,ReLU,不少读者都已经非常熟悉。但是你是否曾想过这几个问题:

    SIGAI学习与实践平台

扫码关注云+社区

领取腾讯云代金券