纯函数

引言

  • 对于函数,大家都不陌生,但是对于纯函数可能有些生疏。我们在写函数的时候,会肆无忌惮,充分的利用js的一些特性,比如使用作用域去修改外部变量,对象属性,数据结构等。这样写如果稍不注意,可能会出现隐藏的bug,使我们的函数,不再是一个可信赖的函数。而纯函数正是一个可以保证数据信赖的函数方式,他也是一个函数,只不过有他特有的要求规范,下面就让我们从函数的副作用开始,来学习纯函数的优缺点

什么是纯函数?

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

比如常用的slicesplice,这两个函数的作用别二致。但是slice符合纯函数的定义,因为对于相同的输入,他能保证相同的输出。而splice却会嚼烂调用他的那个数组,然后吐出来;这样就会产生观察到的副作用

let arr = [1,2,3]
// 纯的
arr.slice(0, 3) // (3) [1, 2, 3]
arr.slice(0, 3) // (3) [1, 2, 3]
arr.slice(0, 3) // (3) [1, 2, 3]// 不纯的
arr.splice(0, 3) // (3) [1, 2, 3]
arr.splice(0, 3) // []

在函数式编程中,我们讨厌这种会改变数据的笨函数。我们追求的是那种可靠的,每次都能返回同样结果的函数,而不是像 splice 这样每次调用后都把数据弄得一团糟的函数,这不是我们想要的。

来看另外一个例子

// 不纯的
let immutableState = 21
const checkAge = (age) => {return age >=immutableState
}// 纯的
const checkAge = (age) => {let immutableState = 21return age >=immutableState
}

在不纯的版本中,checkAge的结果取决于immutableState这个变量是不是一个可变的值。 如果是可变的,那么对于immutableState的追踪,将会是一件棘手的事。我们可以通过参数的形式传递immutableState,也可以通过作用域的方式,限制变量的访问范围,当然我们也可以通过一些手段,使immutableState成为一个不可变(immutable) 对象.要实现这个效果,必须得创建一个对象,然后调用 Object.freeze 方法:

let immutableState = Object.freeze({immutableState: 21
})

什么是函数的副作用

如果函数与外部可变状态进行交互,则他是有副作用的

  • 任何修改外部的变量,对象属性,数据结构
  • 控制台(屏幕)输入输出的交互
  • 文件操作,网络操作
  • 抛出异常或错误终止
  • …不止于此

站在纯函数的角度来说,以上均是非纯函数的副作用。我们也不必太刻意使用纯函数,因为有很多功能,还是得依赖非纯函数来实现,毕竟你要和这个世界打交道,而不是只活在自己的世界里。

函数副作用demo - 输出最大数

let arrScore = [1, 3, 2, 5, 4];
const getMaxScore = () => {arrScore = arrScore.sort((a, b) => {return b - a;});return arrScore[0];
};
console.log('getMaxScore', getMaxScore()); // 5
console.log('arrScore', arrScore); //  [ 5, 4, 3, 2, 1 ]

问题分析

  1. 在函数内,直接使用了外部变量,并且sort会改变源数据。下次再使用源数据,数据处于不可知状态

问题解决

  1. 通过参数的形式,将数据传入
  2. 参数如果是引用类型,提前进行clone
  3. 避免使用修改源数据的api,使用其他方式实现
let arrScore = [1, 3, 2, 5, 4];
const getMaxScore = (arrScore) => {let temp = arrScore.slice();temp = temp.sort((a, b) => {return b - a;});return temp[0];
};
console.log('getMaxScore', getMaxScore(arrScore)); // 5
console.log('arrScore', arrScore); //  [ 1, 3, 2, 5, 4 ]
let arrScore = [1, 3, 2, 5, 4];
const getMaxScore = (arrScore) => {return Math.max(...arrScore);
};
console.log('getMaxScore', getMaxScore(arrScore)); // 5
console.log('arrScore', arrScore); //  [ 1, 3, 2, 5, 4 ]

追求“纯”的理由

可缓存性 (Cacheable)

  • 由于纯函数的输入就决定了输出,就和数学中的函数一样(所以我们也可以像数学公式一样推导),一个确定的输入对应一个确定的输出,

  • 因此我们可以根据传入的参数把结果缓存起来,这样后续以同样的参数调用的时候就可以直接返回结果,而不是重新执行一遍算法.

  • 实现缓存的一种典型方式是memoize技术,

vue 源码

/*** Create a cached version of a pure function.*/function cached(fn) {var cache = Object.create(null);return function cachedFn(str) {var hit = cache[str];return hit || (cache[str] = fn(str));};}/*** Capitalize a string.*/var capitalize = cached(function (str) {return str.charAt(0).toUpperCase() + str.slice(1);});

可移植性,自文档化(Portable / Self-Documenting)

  • 纯函数的依赖很明确,因此更易于观察和理解

  • 因为他们与环境无关,所以可以拷贝到任何地方运行,提高了代码的复用性。

  • 对比面向对象,你从类中拷贝一个方法,就要麻烦得多。

可测试性(Testable)

  • 纯函数让测试更加容易。

  • 只需要给定输入,断言输出就可以了。

  • 甚至有专门的测试工具帮我们自动生成输入,并断言输出。 比如Quickcheck

合理性,引用透明性(Reasonable)

  • 很多人相信使用纯函数最大的好处是引用透明性(referential transparency)。如果一段代码可以替换成它执行所得的结果,而且是在不改变整个程序行为的前提下替换的,那么我们就说这段代码是引用透明的。
var Immutable = require('immutable');var decrementHP = function(player) {return player.set("hp", player.hp-1);
};var isSameTeam = function(player1, player2) {return player1.team === player2.team;
};var punch = function(player, target) {if(isSameTeam(player, target)) {return target;} else {return decrementHP(target);}
};var jobe = Immutable.Map({name:"Jobe", hp:20, team: "red"});
var michael = Immutable.Map({name:"Michael", hp:20, team: "green"});punch(jobe, michael);
//=> Immutable.Map({name:"Michael", hp:19, team: "green"})

在上面的demo中,decrementHPisSameTeampunch都是纯函数,所以是透明引用。我们可以使用一种叫做“等式推到”的技术来分析代码
首先内联isSameTeam

var isSameTeam = function(player1, player2) {return player1.team === player2.team;
};// 因为数据是不可变的,所以我们直接把team 替换为实际的值var punch = function(player, target) {if("red" === "green") {return target;} else {return decrementHP(target);}
};// if 执行的结果是false,所以可以把整个if 语句删掉var punch = function(player, target) {return decrementHP(target);
};
// 然后再内联decrementHP,punch就编程一个让hp的值减1的调用

并行运算

  • 我们可以并行运行任意纯函数。因为纯函数根本不需要访问共享的内存,而且根据其定义,纯函数也不会因副作用而进入竞争态(race condition)。

  • 并行代码在服务端 js 环境以及使用了 web worker 的浏览器那里是非常容易实现的,因为它们使用了线程(thread)。不过出于对非纯函数复杂度的考虑,当前主流观点还是避免使用这种并行。

javascript 中的纯函数相关推荐

  1. 在javascript中使用纯函数处理副作用

    在javascript中使用纯函数处理副作用 今天给大家带来一片译文, 详情请点击这里.可能在墙内哦 开始了, 如果你点开这篇文章, 就证明你已经开始涉及函数式编程了, 这距离你知道纯函数的概念不会很 ...

  2. JavaScript中的纯函数是什么?

    Pure functions are the atomic building blocks in functional programming. They are adored for their s ...

  3. JavaScript中的工厂函数vs构造函数vs class

    原文链接:JavaScript Factory Functions vs Constructor Functions vs Classes 作者:Eric Elliott 译者:sunny 转载需提前 ...

  4. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  5. 关于javascript中的回调函数

    关于javascript中的回调函数 原文地址:http://blog.csdn.net/sicluoyi/article/details/1737969 考虑一个这样的例子: 假如某个项目的底层和高 ...

  6. 深入认识javascript中的eval函数

    来源:http://wanyij.blog.51cto.com/46570/43794 发现为本文起一个合适的标题还不是那么容易,呵呵,所以在此先说明下本文的两个目的: (1)介绍javascript ...

  7. JavaScript中语句与函数的执行辨析

    文章出自个人博客https://knightyun.github.io/2018/05/23/js-anonymous-function,转载请申明. Javascript代码中,语句和函数以及匿名函 ...

  8. 如何找到JavaScript中的调用者函数?

    function main() {Hello(); }function Hello() {// How do you find out the caller function is 'main'? } ...

  9. jQuery中的read 和JavaScript中 的onload函数的区别

    2019独角兽企业重金招聘Python工程师标准>>> 在JavaScript中,onload函数是最经常用到的,这个函数的作用是等待网页完全加载之后再去执行代码块中的语句,因为按照 ...

最新文章

  1. opensuse 安装java_OpenSUSE Leap 42.3 安装java(Oracle jre)
  2. android 多用户管理UserManager
  3. Android 聊天软件客户端
  4. Win10系列:JavaScript图形
  5. Vue实现仿音乐播放器7-实现音乐榜单效果
  6. boost::multiprecision模块Eigen相关的测试程序
  7. 爬取json Swaggerui界面
  8. IDEA必备插件提高企业开发效率(强烈推荐)
  9. 【python】使用枚举类
  10. VBA中的数据字典,可以理解为Java中的Map
  11. matlab struct 结构体
  12. [转]ffmpeg库音频解码示例
  13. python---windows中的文件路径书写的错误
  14. 「数字电子技术基础」6.触发器
  15. 数据挖掘导论思维导图
  16. mysql rds 迁移_如何实现迁移RDS for MySQL数据到本地 MySQL
  17. 移动应用开发--实现QQ登录界面(Android)
  18. 3G杀手应用还是全方位轰炸?
  19. CPU占用率百分百原因及解决方法
  20. [元带你学NVMe协议] NVME 2.0 新技术解决了什么痛点? 会带来哪些变革?

热门文章

  1. 本科、硕士、博士的之间有何区别?
  2. android开发系列之性格测试,性格色彩测试android程序开发之十--输出结果
  3. 产品经理应该学习墨刀还是Axure?
  4. 财经股票数据之网络爬虫技术
  5. 华为p50官方首曝鸿蒙,华为P50,会是鸿蒙系统的首发手机吗?
  6. 教你学会网易云JS逆向,爬来的歌打包发给女友邮箱可好?
  7. TLD7002学习笔记(一)-芯片介绍
  8. telnet怎么算成功_男人一生怎么才算成功?
  9. 酒店智能联网门锁解决方案
  10. BYOD -- 企业的困境与力量