函数式编程(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)
}
}
【完】✅