• 常见的函数式编程模型


    1.闭包(Closure)

    闭包的概念

    可以保留局部变量不被释放的代码块,被称为一个闭包。

    闭包的特点:函数嵌套函数、内部函数可以引用外部函数的参数和变量、参数和变量不会被垃圾回收机制收回

    // 创建一个闭包
    function makeCounter() {
      let k = 0;
    
      return function() {
        return ++k;
      };
    }
    
    const counter = makeCounter();
    
    console.log(counter());  // 1
    console.log(counter());  // 2

    makeCounter 这个函数的代码块,在返回的函数中,对局部变量 k ,进行了引用,导致局部变量无法在函数执行结束后,被系统回收掉,从而产生了闭包。而这个闭包的作用就是,“保留住“ 了局部变量,使内层函数调用时,可以重复使用该变量;而不同于全局变量,该变量只能在函数内部被引用。 

    换句话说,闭包其实就是创造出了一些函数私有的 ”持久化变量“。 

    所以从这个例子,我们可以总结出,闭包的创造条件是: 

    (1)存在内、外两层函数    (2)内层函数对外层函数的局部变量进行了引用

    闭包的用途

    闭包的主要用途就是可以定义一些作用域局限的持久化变量,这些变量可以用来做缓存或者计算的中间量等等。

    // 简单的缓存工具
    // 匿名函数创造了一个闭包
    const cache = (function() {
      const store = {};
      
      return {
        get(key) {
          return store[key];
        },
        set(key, val) {
          store[key] = val;
        }
      }
    }());
    
    cache.set('a', 1);
    cache.get('a');  // 1

    上面例子是一个简单的缓存工具的实现,匿名函数创造了一个闭包,使得 store 对象 ,一直可以被引用,不会被回收。 

    闭包的弊端:持久化变量不会被正常释放,持续占用内存空间,很容易造成内存浪费,所以一般需要一些额外手动的清理机制。

    2.高阶函数 

    接受或者返回一个函数的函数称为高阶函数,JavaScript 中见到许多原生的高阶函数,例如 Array.map , Array.reduce , Array.filter 

    以map函数方法为例:

      map:映射是对集合而言的,即把集合的每一项都做相同的变换,产生一个新的集合

    // 数组中每一项加一,组成一个新数组
    
    // 一般写法
    const arr = [1,2,3];
    const rs = [];
    for(const n of arr){
      rs.push(++n);
    }
    console.log(rs)
    
    // map改写
    const arr = [1,2,3];
    const rs = arr.map(n => ++n);

    上面一般写法,利用 for...of 循环的方式遍历数组会产生额外的操作,而且有改变原数组的风险 
    而 map 函数封装了必要的操作,使我们仅需要关心映射逻辑的函数实现即可,减少了代码量,也降低了副作用产生的风险。

    柯里化(Currying)

    给定一个函数的部分参数,生成一个接受其他参数的新函数,叫做函数的柯里化。

    柯里化可以使我们只关心函数的部分参数,使函数的用途更加清晰,调用更加简单。 

    //一般写法
    function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
      if( n <= 1 ) {return ac2};
    
      return Fibonacci2 (n - 1, ac2, ac1 + ac2);
    }
    // 柯里化
    function currying(fn, n1, n2) {
      return function (m) {
        return fn.call(this, m, n1, n2);
      };
    }
    function tailFibonacci(n, ac1, ac2){
      if( n <= 1 ) {return ac2};
    
      return tailFibonacci (n - 1, ac2, ac1 + ac2);    
    }
    const Fibonacci4 = currying(tailFibonacci,1,1);
    
    Fibonacci4(100)  // 573147844013817200000

    4.组合

    将多个函数的能力合并,创造一个新的函数,叫做组合。

    // 数组中每个单词大写,做 Base64
    
    // 一般写法 (其中一种)
    const arr = ['pen', 'apple', 'applypen'];
    const rs = [];
    for(const w of arr){
      rs.push(btoa(w.toUpperCase()));
    }
    console.log(rs);
    
    
    // _.flow 改写
    const arr = ['pen', 'apple', 'applypen'];
    const upperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa));
    console.log(upperAndBase64(arr));

    _.flow 将转大写和转 Base64 的函数的能力合并,生成一个新的函数。方便作为参数函数或后续复用。 

    总结

    我理解的 JavaScript 函数式编程,可能和许多传统概念不同。我并不只认为 高阶函数 算函数式编程,其他的诸如普通函数结合调用、链式结构等,我都认为属于函数式编程的范畴,只要他们是以函数作为主要载体的。 

    而我认为函数式编程并不是必须的,它也不应该是一个强制的规定或要求。与面向对象或其他思想一样,它也是其中一种方式。我们更多情况下,应该是几者的结合,而不是局限于概念。 

    参考来源:我眼中的 JavaScript 函数式编程

  • 相关阅读:
    Oracle 备份与恢复介绍
    Oracle 监听器
    ORA-01041: 内部错误,hostdef 扩展名不存在
    NIO读写文件并加锁
    ActiveMQ消息生产消费流程
    金额,有效值等保留小数位处理
    JVM
    Linux架构分布式集群之基础篇
    Vue.js 开发实践:实现精巧的无限加载与分页功能
    Mysql 查看连接数,状态
  • 原文地址:https://www.cnblogs.com/guorange/p/7146399.html
Copyright © 2020-2023  润新知