JavaScript函数式编程学习笔记

函数式编程

1. 什么是函数式编程

函数式编程(英语:functional programming)或称函数程序设计、泛函编程,是一种编程范式,它将计算机运算视为函数运算,并且避免使用程序状态以及易变对象。即对过程进行抽象,将数据以输入输出流的方式封装进过程内部,从而也降低系统的耦合度。

非函数式编程

myString="my name is String"
var words = [],
  count = 0;
text = myString.split(" ");
for (i = 0; count < 4, i < text.length; i++) {
  if (!text[i].match(/[0-9]/)) {
    words = words.concat(text[i]);
    count++;
  }
}
console.log(words); // ["my", "name", "is", "String"]

函数式编程

myString="my name is String"
var words = myString
  .split(" ")
  .filter(function(x) {
    return !x.match(/[1-9]+/);
  })
  .slice(0, 4);
console.log(words); // ["my", "name", "is", "String"]

2. 纯函数

“函数”是输入与输出之间的关系,即对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态

var messages = ["Hi", "Hello", "Sup", "Hey", "Hola"];

messages
  .map(function(s, i) {
    return printSomewhere(s, 100 * i * 10, 100 * i * 10);
  })
  .forEach(function(element) {
    document.body.appendChild(element);
  });

3. lambda函数又称匿名函数

如果函数只需要引用一次,则无需浪费函数名

匿名函数lambda:lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象。是指一类无需定义标识符(函数名)的函数或子程序。所谓匿名函数,通俗地说就是没有名字的函数,lambda函数没有名字,是一种简单的、在同一行中定义函数的方法

// 在JavaScript中
const lamFun = (x)=> {return x+1}

// 在JAVA中
() -> System.out.println("hello world");

// 在Python中
fun = lambda x, y: x * y
print(fun(2, 3))  // 6

4. abamdan柯里化

所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。

// 柯里化之前
function add(x, y) {
  return x + y;
}

add(1, 2) // 3

// 柯里化之后
function addX(y) {
  return function (x) {
    return x + y;
  };
}

addX(2)(1)

5. 函数合成(compose)

如果一个值要经过多个函数,才能变成另外一个值,就可以把所有中间步骤合并成一个函数,这叫做"函数的合成"(compose)。
合成也是函数必须是纯的一个原因

var compose = function(f,g) {
  return function(x) {
    return f(g(x));
  };
};

var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };
var shout = compose(exclaim, toUpperCase);

shout("send in the clowns"); // "SEND IN THE CLOWNS!"

6. 函子、容器Functor

简单的例子

var Container = function(x) {
  this._value = x
}
Container.of = x => new Container(x)
Container.of(1)  
Container.prototype.map = function(f){
  return Container.of(f(this._value))
}
image.png
class Functor {
  constructor(val) { 
    this.val = val; 
  }

  map(f) {
    return new Functor(f(this.val));
  }
}

Functor是一个函子,它的map方法接受函数f作为参数,然后返回一个新的函子,里面包含的值是被f处理过的(f(this.val))
一般约定,函子的标志就是容器具有map方法。该方法将容器里面的每一个值,映射到另一个容器。
尾调用:指某个函数的最后一步是调用另一个函数

7. 自调用函数和闭包

闭包指的是有权访问父作用域的函数,即使在父函数关闭之后。

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

8. 递归

欧几里得算法,寻找两个数的最大公分母

function gcd(a, b) {
  if (b == 0) {
    // base case (conquer)
    return a;
  } else {
    // recursive case (divide)
    return gcd(b, a % b);
  }
}
console.log(gcd(12, 8));
console.log(gcd(100, 20));

9. 高阶函数

函数可以作为函数被传入,也称为回调函数,如函数合成运算。
可以返回函数作为输出,如函数柯里化运算。
高阶函数是一个接收函数作为参数或将函数作为输出返回的函数

add = (x) => (y) => x + y

add(10)  //(y) => x + y
add(10)(20) // 30

高阶函数的复杂实例

function powersOf(x) {
  return function(y) {
    // this is an anonymous function!
    return Math.pow(x, y);
  };
}
powerOfTwo = powersOf(2);
console.log(powerOfTwo(1)); // 2
console.log(powerOfTwo(2));

缺点:在堆栈中难以识别,不方便调试

荣誉函数

map() filter() reduce() 这三个函数为函数式编程带来便利

实现一个类似Map的高阶函数

function mapForEach(arr,fn){
  const newArray = []
  for(let i=0;i{ return item.length })
console.log(arrMap) //[3, 5, 6, 4, 5]

10. 学习完自己的代码优化实例

多维数组遍历

let arr4 = [{name:'one',children:[{name:'two',children:[{name:'three'}]}]}]

function renderList(arrs){
  const list = []
  function renderArr(arr=[]){
   arr.forEach(item=>{
     list.push({name:item.name})
     if(item.children && item.children.length){
       renderArr(item.children)
     }
   })
  }
 renderArr(arrs)
 return list
}
console.log('lists',renderList(arr4))
// [0: {name: "one"},1: {name: "two"},2: {name: "three"}]

源数据结构如下:


image.png
const data = [{
    id: 5,
    label: "工业",
    children:[
        {   id: 10012,
            label: "塑料加工",
            children:[
                {id: 10000129,label: "共混料"},
                {id: 10000130,label: "母料(色母等)"},
                {id: 10000301,label: "木塑复合材料"}
            ]
        }
    ]
}]

未优化前

renderTreeList ({ list = [], isOpen = false, rank = 0, parentId = [] }) {
      list.forEach(item => {
        this.treeList.push({
          id: item.id,
          label: item.label,
          rank,
          parentId,
          isOpen: false,
          show: rank === 0,
          checked: this.selectedIds.includes(item.id)
        })
        if (Array.isArray(item.children) && item.children.length > 0) {
          let parents = [...parentId]
          parents.push(item.id)
          this.renderTreeList({ list: item.children, rank: rank + 1, parentId: parents })
        } else {
          this.treeList[this.treeList.length - 1].lastRank = true
        }
      })
      this.treeList = [...this.isDefaultOpen({ arr: this.treeList, selectedIds: this.selectedIds, isOpen })]
    }
    

优化后

 renderList (arrs) {
      const arrList = []
      const renderArr = function ({ list = [], isOpen = false, rank = 0, parentId = [] }) {
        list.forEach(item => {
          arrList.push({
            id: item.id,
            label: item.label,
            rank,
            parentId,
            isOpen: false,
            show: rank === 0,
            checked: true
          })
          if (Array.isArray(item.children) && item.children.length > 0) {
            let parents = [...parentId]
            parents.push(item.id)
            renderArr({ list: item.children, rank: rank + 1, parentId: parents })
          } else {
            arrList[arrList.length - 1].lastRank = true
          }
        })
      }
      renderArr({ list: arrs })
      return arrList
    }
    

es6数组拉平方法

https://es6.ruanyifeng.com/?search=%E4%B8%80%E7%BB%B4&x=0&y=0#docs/array#%E6%95%B0%E7%BB%84%E5%AE%9E%E4%BE%8B%E7%9A%84-flat%EF%BC%8CflatMap

总结:

函数式编程的好处:

1.不容易产生bug,方便测试和并行处理;
2.可以抛弃this,避免被this指向弄晕;
3.打包过程中可以更好的利用 tree shaking 过滤无用代码;
4.有很多库可以帮助我们进行函数式开发,比如经典的lodash。

缺点:

1.存在性能问题,Map、filter这些需要遍历多次,增加时间开销
2.资源占用,在 JS 中为了实现对象状态的不可变,往往会创建新的对象,因此,它对垃圾回收所产生的压力远远超过其他编程方式

你可能感兴趣的:(JavaScript函数式编程学习笔记)