一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情
函数式编程(Function Programming, FP) 是编程范式之一。
编程范式即编程规范,编程风格
我们常听听说的编程范式还有:
- 面向对象:典型的就是
java
语言,目前javascript
的超集typescript
也是走这条路线。也就是把事物抽象成程序世界中的类和对象,通过封装、继承和多态来演示事物之间的联系。
- 面向过程:典型的就是
C
语言。在面向过程程序设计中,问题被看成是一些列需要完成的任务,函数则用于完成这些任务,可以看成一步步升级打怪。
而我们今天要讨论的编程范式 -- 函数式编程把事物和事物直接的联系抽象到程序世界,强调的是函数的计算,对运算过程进行抽象。使用函数进行编程。
纯函数
纯函数 指相同的输入永远得到相同的输出,而且没有副作用。
如下: slice
是纯函数,splice
就不是。
let arr = [1, 2, 3]; console.log(arr.slice(0,1)); // 1 console.log(arr.slice(0,1)); // 1 console.log(arr.slice(0,1)); // 1 let another_arr = [3, 4, 5]; console.log(another_arr.splice(0,1)); // 3 console.log(another_arr.splice(0,1)); // 4 console.log(another_arr.splice(0,1)); // 5 复制代码
纯函数是函数编程的重点。函数是函数编程的一等公民。
一等公民,指函数跟其他类型具有相同的地位,也就是说
function
可以当作令一个function
的参数,也可以作为function
的返回值,也可以作为变量。
比如:
function demo(cb) { cb() } function add(x) { return function(y) { return x + y } } const val = () => 'jimmy'; 复制代码
声明式编程
函数式编程属于声明式编程范式。
声明式编程范式:会描述一些列的操作,但是并不会暴露他们是如何实现的或者数据流是如何传递的。
比如:
let arr = ['hello', 'world', '!'] // 命令式,暴露具体实现 for(let i = 0; i < arr.length; i++) { arr[i] = arr[i].toUpperCase() } // 声明式,隐藏细节 arr.map(str => str.toUpperCase()) 复制代码
引用透明
引用透明(Referential Transparency)指的是一个函数穿传入相同的参数,不管运算多少次,永远会得到相同的值,并且不对外部世界产生任何改变(上面我们已经提到过,就是不产生副作用)。
例子可以参考上面纯函数的例子。
又比如:
function change_style() { let dom = document.getElementById('demo'); dom.style.color = 'red'; } // change_style 对外界产生的副作用,更改了 dom 的样式 复制代码
不可变性
不可变性,就是数据一旦创建后就不会再改变的,所有对不可变性的数据操作返回的是另一个不可变性的数据。这好比操作 const
定义变量一样。
又比如:
let obj = { name: 'jimmy' } let person = obj; person.name = 'Jimmy'; console.log(obj.name); // Jimmy // 此时,对象 obj.name 数据已经发生了改变(可变) let obj1 = { name: 'jimmy' } let person1 = { ...obj, name: 'Jimmy'}; console.log(obj1.name); // jimmy // 此时,对象 obj.name 数据没发生变化(不可变) 复制代码
好了,我们来总结下函数式编程应该具有的特征:
- 纯函数
- 函数是一等公民
- 声明式编程
- 引用透明
- 不可变性
以下面的节流函数应用结束本文:
function throttle(fn) { let canRun = true; return function() { if(!canRun) { return; } canRun = false; setTimeout(() => { fn.call(this, arguments); canRun = true; }, 1000) } }