函数式编程
函数式编程概念
函数式编程(Functional Programming,FP),编程范式之一,同类范式:面向对象编程,面向过程编程
注:函数式编程中的函数指的不是程序中的函数(方法),而是数学中的
函数映射关系
思维方式:
- 面向对象编程:
把现实世界中的事物抽象成程序世界中的类和对象,通过封装、继承、多态来表示事物之间的联系
- 面向函数式编程
把现实世界中的事物和事物之间的联系抽象到程序世界,是对运算过程的抽象
疑问点:函数式编程是对运算过程的抽象,是否是等价于或类似于面向过程编程?
解惑:面向过程编程是分析出解决问题所需要的步骤,然后用函数一步步的实现,使用时依次调用,存在变量状态和相互依赖,属于串行;
而函数式编程更接近于数学计算,着眼于实现,无状态,无副作用,因此不等价
函数式编程特点
- 纯函数
- 相同的输入总是得到相同的输出
- 可以重复使用
函数是 一等公民
- 函数可以储存在变量中
- 函数可以作为参数
- 函数可以作为返回值
高阶函数(Higher order function)
可以把函数作为参数传递给另一个函数(可以使函数更灵活) 函数可以作为返回
高阶函数意义
抽象可以屏蔽细节,只需要关注与目标相关即可,高阶函数是用来抽象通用问题的一种方式
常用高阶函数
forEach/filter/map/every/some...
- every函数:依次检测数组中每个元素,当有一个不满足则返回false,且后续元素不再检测
- every实现:
const every = (array, fn) => {
let flag = true;
for (let value of array) {
flag = fn(value);
if (!flag) {
break;
}
}
return flag;
}
- some函数:依次检测数组中每个元素,当有一个元素满足指定条件时就返回true,否则返回false,且后续元素不再检测
- some实现:
const some = (array, fn) => {
let flag = false;
for (let value of array) {
flag = fn(value);
if (flag) {
break;
}
}
return flag;
}
闭包
函数在执行的时候会放到一个执行栈上,当函数执行完毕之后会从执行栈上移除,但是堆上的作用域成员被外部引用不能释放,因此内部函数依然可以访问外部函数成员的机制为闭包;
通俗的理解,闭包可以让你在一个内层函数中访问到其外层函数的作用域,外层执行返回内层函数后,因存在引用关系,因此内层与外层作用域不会被释放。
疑问点:当外部函数被执行后有变量接收了,此时存在变量到函数的引用关系,无法被释放回收;但如果仅仅执行,不使用变量进行接收,此时是否存在引用关系?会被整体释放回收吗?这个堆上的作用域成员是否为这个变量到对象的引用?
解惑:
1、闭包发生时缓存的是和闭包内部相关的内容,如变量、函数等其余不相关内容会根据浏览器GC机制进行回收
2、和是否有变量存储无关,当函数被调用时,内部函数可以访问外层函数作用域,且访问了某些属性,此时构成闭包,如未存在访问、引用关系,则只是嵌套函数or高阶函数执行,不存在闭包
纯函数
相同的输入永远得到相同的输出,而且没有任何可观察的副作用;
函数式编程不会保留计算中间的结果,所以变量是不可变的
纯函数的好处
- 可缓存
- 可测试
- 并行处理(es6+ 新增web worker,多线程处理)
副作用
如果纯函数依赖外部的状态,就无法保证相同的输出,就会带来副作用
副作用来源
- 配置文件
- 数据库
- 用户的输入
柯里化
当一个函数有多个参数的时候,先传递一部分参数调用它(这部分参数以后不会改变),然后返回一个新的函数接收剩余的参数并返回。
总结:
1、柯里化可以通过给一个函数传递较少的参数得到一个已经记住了某些固定参数的新函数(这是一种对函数参数的缓存,闭包)
2、让函数变得更灵活,让函数的颗粒度更小
3、可以多元函数转换成一元函数,可以组合函数产生强大的函数
柯里化原理模拟
function curry(fn) {
return function newCurry (...args) {
if (args.length < fn.length) {
return function() {
return newCurry(...args.concat(Array.from(arguments)))
}
}
return fn(...args)
}
}
额外知识点
-
delete操作符,用于删除对象的某个属性,如果没有指向这个属性的引用,那么它最终会被释放 -
delete操作符对于所有情况都是返回true,除非属性是一个自身的、不可配置的属性,此时,非严格模式下会返回false,在严格模式下,会抛出TypeError错误注意点: 1、如果删除的属性不存在,则delete不会生效,依然会返回true 2、如果对象的原型链上有一个与待删除属性同名的属性,那么删除属性后,对象会使用原型链上的那个属性,也就是说,delete只会在自身的属性上起作用 3、任何使用var声明的属性不能从全局作用域或函数的作用域中删除,这样的话,delete操作不能删除任何在全局作用域中的函数 4、任何用let或const声明的属性不能从它被声明的作用域中删除 5、不可设置的属性不能被删除,意味着像Math、Array、Object内置对象的属性以及使用Object.definedProperty()方法设置为不可设置的属性不能被删除