JavaScript函数式编程之深入理解纯函数

简介:

更多相关内容见博客 github.com/zhuanyongxi…

纯函数是函数式编程的基础,需要重点理解。纯函数的作用,可以看《JavaScript函数式编程之为什么要函数式编程(非严谨技术层面的扯淡)》

纯函数的概念:

纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。

他的重点在于“相同的输入,永远会得到相同的输出”,后面所说的副作用也是为了满足这一点。

在详细说明纯函数之前,先讲两个其他的概念:可变数据类型和不可变数据类型。

我们知道,在JavaScript中,基本数据类型都是不可变的,他们只能被替换,不能被修改。例如当我们在对字符串进行操作的时候,我们并不能改变这个字符串本身。



var str = 'I am hero';
console.log(str.toUpperCase());	// "I AM HERO"
console.log(str);	//"I am hero"

我们能做的只有把返回的新字符串重新赋值给变量。


var str = 'I am hero';
str = str.toUpperCase();	// "I AM HERO"

而引用数据类型都是可变的,存在变量中的仅仅就是一个地址。对于可变特性,facebook的immutable.js做了针对性的强化处理,此外还有clojurescript这样更加彻底的方式。

为什么要说这两个概念呢?

先不说在JS运行的系统环境中可能会产生副作用,单单看这些可变的数据类型,就会增加我们写纯函数的难度,要十分注意,个别情况我们只能选择接受。

这样的话,在JS中,我们不妨把纯函数换一种方式理解,不要把它当做一个只有“完全满足要求”和“不满足要求”的标准,而要把它想象成一个范围,在这里有高低不同程度的纯函数。

如何理解“相同的输入,永远会得到相同的输出”

文章开头的纯函数的概念中的“永远”可能会让你疑惑,要把它放在词法作用域中考虑,也就是说不考虑再下次执行之前修改常量这一类的情况。

例一



var a = 5;
function A(b) {
  return a + b;
}
A(5);

A函数是一个纯函数吗?显然非常不纯,在程序执行的过程中,变量a很容易被修改,这就会导致每次调用A(5)的返回值改变。

例二

对例一稍作修改

const a = 5;
function A(b) {
  return a + b;
}
A(5);

这是纯函数,确定的输入,确定的输出。

例三

把例二数字常量换成对象


const obj = {id: 5};
function A(_obj) {
  return _obj.id;
}
A(obj);

函数A基本上是纯函数,为什么说是“基本上”?因为有极端情况如下



var obj = {
  get id() {
    return Math.random();
  }
}

注意,obj在传进函数A之前是确定的,getter是在取值的时候才会执行,但是返回的结果是不确定的,所以这个时候函数A就不是纯函数了。随机数和Date都会造成函数不纯,使用的时候要注意。

除此之外,由于对象是可变数据类型,我们在每次输入变量obj到函数A中时,并不具有绝对的自信会返回确定的值。可对于函数A来说,它只关心传进来的数据是什么,而对于那个数据来说,只要不是上面的极端情况,返回的是确定的值。

例四


const obj = {a: 5};
function A(b) {
  return obj.a + b;
}
A(5);

这个很明显很不纯,同例一,注意与例三的区别。

例五

对例四稍作修改


const obj = Object.freeze({a: 5});
function A(b) {
  return obj.a + b;
}
A(5);

这样就纯多了,可是需要注意的是, Object.freeze() 这个方法无法冻结嵌套的对象,例如
const obj = Object.freeze({a: {a: 5}});
obj.a.a = 10;
function A(b) {
  return obj.a.a + b;
}
console.log(A(5));	// 15,不纯

例六



function foo(x) {
  return bar(x);
}

function bar(y) {
  return y + 1;
}

foo(1);

都纯。

例七


function A(a) {
  return function(b) {
    return a + b;
  }
}
var B = A(5);

函数A和函数B是纯函数吗?首先来看函数A,每次输入一个参数,都会得到一个用这个参数组成的函数,得到的函数是固定的,所以函数A是纯函数;再来看函数B虽然看起来好像使用了一个自己外部的变量a,而且这个变量a可能会经常改变,可是,函数B一定要在调用了函数A之后才能得到,并且得到了之后,变量a是无法改变的,这就很纯了。

即便在返回函数B之前修改了a,例如

例八


function A(a) {
  a = 0;
  return function(b) {
    return a + b;
  }
}
var B = A(5);

结论也是一样的。

可如果这样写

例九


function A(a) {
  return function(b) {
    return a = a + b;
  }
}
var B = A(5);

关于副作用,见《JavaScript函数式编程之副作用》

原文发布时间为:2018年06月08日
原文作者:砖用冰西瓜

本文来源: 掘金 如需转载请联系原作者




相关文章
|
15天前
|
JavaScript 前端开发 Java
[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
本文介绍了JavaScript中常用的函数和方法,包括通用函数、Global对象函数以及数组相关函数。详细列出了每个函数的参数、返回值及使用说明,并提供了示例代码。文章强调了函数的学习应结合源码和实践,适合JavaScript初学者和进阶开发者参考。
27 2
[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
|
13天前
|
前端开发 JavaScript 开发者
除了 Generator 函数,还有哪些 JavaScript 异步编程解决方案?
【10月更文挑战第30天】开发者可以根据具体的项目情况选择合适的方式来处理异步操作,以实现高效、可读和易于维护的代码。
|
27天前
|
JavaScript 前端开发
JavaScript 函数语法
JavaScript 函数是使用 `function` 关键词定义的代码块,可在调用时执行特定任务。函数可以无参或带参,参数用于传递值并在函数内部使用。函数调用可在事件触发时进行,如用户点击按钮。JavaScript 对大小写敏感,函数名和关键词必须严格匹配。示例中展示了如何通过不同参数调用函数以生成不同的输出。
|
30天前
|
存储 JavaScript 前端开发
JS函数提升 变量提升
【10月更文挑战第6天】函数提升和变量提升是 JavaScript 语言的重要特性,但它们也可能带来一些困惑和潜在的问题。通过深入理解和掌握它们的原理和表现,开发者可以更好地编写和维护 JavaScript 代码,避免因不了解这些机制而导致的错误和不一致。同时,不断提高对执行上下文等相关概念的认识,将有助于提升对 JavaScript 语言的整体理解和运用能力。
|
1月前
|
JavaScript 前端开发
js教程——函数
js教程——函数
32 4
|
1月前
|
存储 JavaScript 前端开发
js中函数、方法、对象的区别
js中函数、方法、对象的区别
16 2
|
1月前
|
JavaScript 前端开发 Java
【javaScript数组,函数】的基础知识点
【javaScript数组,函数】的基础知识点
23 5
|
1月前
|
JavaScript 前端开发
Node.js 函数
10月更文挑战第5天
23 3
|
1月前
|
前端开发 JavaScript
探索JavaScript函数基础
探索JavaScript函数基础
18 3
|
1月前
|
存储 JavaScript 前端开发
JavaScript数据类型全解:编写通用函数,精准判断各种数据类型
JavaScript数据类型全解:编写通用函数,精准判断各种数据类型
19 0