虽然我是计算机方向毕业的学生, 但是认识到纯函数, 还是在学 react 的时候...
相同的输入总会得到相同的输出, 并且不会产生副作用的函数, 就是纯函数.
我们可以通过一个是否会改变原始数据的两个同样的功能的方法来区别纯函数与非纯函数之间的不同.
希望有能有这么一个函数, 能够获取到引入数组的最后一项. 那么可以通过以下两种方式来实现.
- 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, 原数组发生改变
getLast 与 getLast_虽然都能够获得数组的最后一项值, 但是 getLast_改变了原数组. 而当原数组被改变, 我们再次调用该方法时, 得到的结果就会变得不一样. 这种不可预测的封装方式是非常糟糕的, 它会把我们的数据搞的非常混乱. 在 JavaScript 原生支持的数据方法中, 也有许多不纯的方法, 我们在使用时要多加警惕, 要清晰地知道原始数据的改变是否会留下隐患.
- 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(); // 非纯函数
- source.reverse(); // 非纯函数
与这种会改变原始数据的函数相比, 纯函数明显更加可靠. 很显然我们都不希望自己的数据在经过几次调用后就变得一团糟.
纯函数还有一个重要的特点, 那就是除了传入的参数外, 不依赖任何外界的信息与状态. 如下面这个例子.
- var name = 'pan';
- function sayHello() {
- return 'Hello,' + name;
- }
- sayHello(); //Hello, pan
- // 当我们有其它需求时需要改变 name 的值
- name = 'zhang';
- sayHello(); //Hello, zhang
同样的调用, 但是由于 sayHello 函数依赖于外界的 name 变量, 因此当外界变量发生变化时, 函数的运行结果就变得不一样. 很显然这并不是我们封装函数时所希望看到的状况, 因为这样的变化是不可预测的. 因此, 对于上面的例子, 我们应该把 name 当作一个参数传入, 这样就能够直观地看到该函数执行时会输出的结果了.
- function sayHello(name) {
- return 'Hello,' + name;
- }
1. 纯函数的可移植性
在封装一个函数, 一个库或一个组件时, 其实都期望一次封装, 多处使用, 而纯函数刚好具备这样的特性.
纯函数不依赖参数之外的值, 因此纯函数的依赖非常明确. 也正是如此, 我们才能够把一些常用的功能封装成一个公共方法, 这样以后遇到类似的场景时就不用再重新封装了.
我们知道一个页面的 URL 里常常会在'?'后面带有参数, 例如 https://www.baidu.com/s?word=javascript&tn=02003390_7_hao_pg. 很多时候我们需要从这段 URL 中, 获取某些参数对应的值. 例如, 这个例子中的'word'的值为 javascript. 那么想要封装这样一个纯函数, 应该怎么做了?
- 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?word=javascript&tn=02003390_7_hao_pg';
- getParams(url, 'word'); //javascript
虽然 getParams 并非完全健壮, 但是已经足以体现纯函数可移植的特点. 我们可以在任何需要从 url 中取得参数对应值的地方调用该方法.
2. 纯函数的可缓存性
在实践中我们可能会处理大量的数据, 例如根据日期, 得到当日相关的数据, 并处理成前端能够使用的数据. 假设我们封装了一个 process 方法来处理每天的数据, 而这个处理过程会很复杂. 如果不缓存处理结果, 那么每次想要得到当天的数据时, 就不得不从原始数据在转换一次. 当数据的处理足够复杂时, 那么很可能不是性能最优的解决方案. 而纯函数的特点是, 相同的输入总能得到相同的输出, 因此如果将处理过每一台年的数据缓存起来, 那么第二次或者更多次的想要得到的当天的数据时, 就不用经历复杂的处理过程了.
- // 传入日期, 获取当天的数据
- function process(date) {
- var result = '';
- // 略掉中间复杂的处理过程
- return result;
- }
- function withProcess(base) {
- var cache = {};
- return function() {
- var date = arguments[0];
- if(cache[date]) {
- return cache[date];
- }
- return base.apply(base, arguments);
- }
- }
- var _process = withProcess(process);
- // 经过上面一句代码处理之后, 就可以使用_process 来获取我们想要的数据了.
- // 如果数据存在, 就返回缓存中的数据, 如果不存在, 则就调用 process 方法重新获取.
上面利用了闭包的特性, 将处理过的数据都缓存在了 cache 中. 这种方式算是高阶函数的运用了.
什么是纯函数, 纯函数有什么特点, 以及为什么要尽量使用纯函数想必这个记录也算完整了.
虽然在实践中并不是所有的场景都能够使用纯函数, 但还是应尽量在合适的场景使用它.
感谢阳波大神.
这些都是我以往的学习笔记. 如果您看到此笔记, 希望您能指出我的错误. 有这么一个群, 里面的小伙伴互相监督, 坚持每天输出自己的学习心得, 不输出就出局. 希望您能加入, 我们一起终身学习. 欢迎添加我的个人微信号: Pan1005919589
来源: https://juejin.im/post/5b231a63e51d4558a04a261d