1.高阶函数
函数当参数,把传入的函数做一个封装,然后返回这个封装
函数,达到更高程度的抽象,即高阶函数。
组件当参数,把传入的组件做一个封装,然后返回这个封装
组件,达到更高程度的抽象,即高阶组件。
var add = function(a,b){
return a + b;
};
function math(func,array){
return func(array[0],array[1]);
}
math(add,[1,2]); // 3
2.尾调用优化PTC
指函数内部的最后一个动作是函数调用。该调用的返回值,直接返回给函数。函数调用自身,称为递归。如果结尾调用自身,就称为尾递归。
递归需要保存大量的调用记录,很容易发生栈溢出错误
while true是只执行当前主线程,不再拉起其他线程,直到执行结束,不会在栈中留任何信息,babel,async 就用到了,而递归直接会爆栈。
//递归
function sum(n) {
if (n === 1) return 1;
return n + sum(n - 1);
}
//尾递归
function sum(x, total) {
if (x === 1) {
return x + total;
}
return sum(x - 1, x + total);
}
//使用尾递归优化,将递归变为循环,那么只需要保存一个调用记录,这样就不会发生栈溢出错误了。
//遗憾的是浏览器并未支持
注意
:
1.尾递归的判断标准是函数运行最后一步是否调用自身,而不是最后一行调用自身,最后一行调用其他函数 并返回叫尾调用。
2.按道理尾递归调用栈永远都是更新当前的栈帧而已,这样就完全避免了爆栈的危险。但是现如今的浏览器并未完全支持,原因有二 ①在引擎层面消除递归是一个隐式的行为,程序员意识不到。②堆栈信息丢失了,开发者难已调试。
3.既然浏览器不支持我们可以把这些递归写成while~
3.闭包
外层的函数执行完毕,栈上的调用帧被释放,但是堆上的作用域并不被释放,仍然可以被访问到,就形成了闭包,闭包一定会造成内存泄漏。
内存泄露的根本原因
就是你的代码中分配了一些‘顽固的’内存,浏览器无法进行回收,如果这些’顽固的’内存还在一直不停地分配就会导致后面所用内存不足,造成泄露。
闭包的内存泄漏原因
是,内部函数被外部函数包含,内部函数将外部函数的局部活动对象添加到自己的作用域中,外部函数执行完毕了,垃圾回收机制,被另一个作用域引用的变量不会被回收,内部函数引用活动对象,活动对象还会保存在内存里面,除非内部的函数解除对活动变量的引用,才可以释放内存,
var innerFunc = outFunc(5)
var result = innerFunc(6)
innerFunc = null
4.范畴、容器、Functor(函子)
范畴
可以想象成是一个容器,里面包含两样东西。值(value)、值的变形关系,也就是函数。
函子
是函数式编程里面最重要的数据类型,也是基本的运算单位和功能单位。它首先是一种范畴,也就是说,是一个容器,包含了值和变形关系。比较特殊的是,它的变形关系可以依次作用于每一个值,将当前容器变形成另一个容器。把东西装进一个容器,只留出一个接口 map 给容器外的函数,map 一个函数时,我们让容器自己来运行这个函数,这样容器就可以自由地选择何时何地如何操作这个函数,就可以实现惰性求值等特性。
//函子
class Functor {
constructor(val) {
this.val = val;
}
map(f) {
return new Functor(f(this.val));
}
}
(new Functor(2)).map(function (two) {
return two + 2;
});// Functor(4)
//Functor是一个函子,它的map方法接受函数f作为参数,然后返回一个新的函子,里面包含的值是被f处理过的(f(this.val))
学习函数式编程,实际上就是学习函子的各种运算。把运算方法封装在函子中,可以衍生出不同类型的函子,运用函数式编程就是运用不同的函子,
point 函子:使用of静态方法实现的函子就是point 函子,就不需要用new 面向对象的方式生成函子了。
class Functor {
constructor(val) {
this.val = val;
}
map(f) {
return new Functor(f(this.val));
}
}
//添加of方法,就是对new Functor的封装
Functor.of = function(val) {
return new Functor(val);
};
Functor.of(2).map(function (two) {
return two + 2;
});// Functor(4)
//把2装进一个Functor容器,调用map,传递一个函数个map,map再利用我们传递的函数修改值
maybe 函子,用来处理异常和错误
var Maybe = function(x) {
this.__value = x;
}
Maybe.of = function(x) {
return new Maybe(x);
}
Maybe.prototype.map = function(f) {
return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value));
}
Maybe.prototype.isNothing = function() {
return (this.__value === null || this.__value === undefined);
};
Maybe.of(null).map(x=>x.toUpperCase())
//本来null.toUpperCase()会报错,这里使用Maybe函数,结果为Maybe {__value: null}
either 函子,可以用来错误处理,也可以用来做逻辑或,Either 函子内部有两个值:左值(Left)和右值(Right)。右值是正常情况下使用的值,左值是右值不存在时使用的默认值,可以代替try…catch
left可以传递出错误消息,在调用链接中,任何一环出问题就会报出错误,就不需要一层一层的使用try…catch了。
AP 函子,applicative 函子的简称,又叫做应用函子,是实现了 ap 方法的 pointed 函子。Ap 函子可以让不同的函子可以相互应用,能够把一种函子的函数值应用到另一个函子的值上。主要是执行函数。
class Ap extends Functor {
ap(F) {
return Ap.of(this.val(F.val));
}
}
Ap.of(addTwo).ap(Functor.of(2))
5.IO
主要是处理异步,真实的世界是脏的,使用IO进行封装,它把不纯的操作(比如IO、网络请求、DOM)包裹到一个函数内,从而延迟这个操作的执行。
6.Monad
表示一个运算过程,通过函数拆解成互相连接的多个步骤。你只要提供下一步运算所需的函数,整个运算就会自动进行下去,可以理解成一个拆箱装箱的过程,promise就是一种monad,避免了嵌套地狱。
7.Applicative Functor
https://www.zhihu.com/topic/20072404/top-answers