JavaScript函数式编程

1.什么是函数式编程

面向过程、面向对象编程一样函数式编程是一种编程范式,主要是利用函数把运算过程封装起来,通过组合各种函数来计算结果。

举个例子,要把字符串functional programming is great 变成每个单词首字母大写,可以这样实现:

var string = 'functional programming is great';

var result = string
  .split(' ')
  .map(v => v.slice(0, 1).toUpperCase() + v.slice(1))
  .join(' ');

上面先用 split 把字符串转换数组,然后再通过 map 把各元素的首字母转换成大写,最后通过 join 把数组转换成字符串。整个过程就是join(map(split(str))),体现了函数式编程的核心思想:通过函数对数据进行转换。

2.命令式和声明式

2.1 命令式

通过编写一条又一条指令去让计算机执行一些动作,一般会涉及到很多繁杂的细节。

// 命令式
var CEOs = [];

for (var i = 0; i < companies.length; i++) {
  CEOs.push(companies[i].CEO)
}

2.2 声明式编程

通过写表达式的方式来声明操作,而非通过一步一步的指示。表达式通常是某些函数调用的复合、一些值和操作符,用来计算出结果值。

// 声明式
var CEOs = companies.map(c => c.CEO);

从上面的例子中,可以看到声明式的写法是一个表达式,无需关心如何进行计数器迭代,返回的数组如何收集,它指明的是做什么,而不是怎么做。

函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。

3.为什么使用函数式编程?

  • 函数式编程能提高复用性和可拓展性;
  • 方便Tree-shaking
    Tree-shaking的本质是通过文档流的引入判断是否使用某个方法(就是没有用到的部分,不进行最后的打包操作),但是面向对象的编程方案无法记录。

4.常见的函数式编程模型?

4.1 闭包

// 简单的缓存工具
// 匿名函数创造了一个闭包
const cache = (function () {
  const store = {};

  return {
    get(key) {
      return store[key];
    },
    set(key, val) {
      store[key] = val;
    }
  }
}());

console.log(cache) // {get: ƒ, set: ƒ}
cache.set('a', 1);
cache.get('a');  // 1

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

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

4.2 高阶函数

高阶函数指的是可以接收函数作为参数或者返回一个新函数的函数。在JS中,常用的高阶函数有map、reduce、filter等,这些函数可以方便地处理数组和对象等数据类型。

// 定义一个高阶函数,接收一个函数作为参数,并调用它
function higherOrderFunc(func) {
    console.log("调用高阶函数");
    func();
}
// 定义一个函数作为参数
function someFunc() {
    console.log("调用了函数作为参数");
}
// 调用高阶函数,并传入函数作为参数
higherOrderFunc(someFunc);

4.3 函数科里化

// 定义一个柯里化函数
function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            // 如果传入参数个数达到函数的形参个数,则直接调用该函数
            return fn.apply(this, args);
        } else {
            // 如果传入参数不足,则返回一个接收剩余参数的新函数
            return function (...rest) {
                return curried.apply(this, args.concat(rest));
            };
        }
    };
}
// 定义一个普通函数,用于演示柯里化
function add(x, y, z) {
    return x + y + z;
}
// 使用柯里化给add函数添加复用性
const curriedAdd = curry(add);

// 调用柯里化函数
console.log(curriedAdd(1, 2, 3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
console.log(curriedAdd(1)(2)(3)); // 6

5.优缺点

优点

  • 更好的管理状态:因为它的宗旨是无状态,或者说更少的状态,能最大化的减少这些未知、优化代码、减少出错情况
  • 更简单的复用:固定输入->固定输出,没有其他外部变量影响,并且无副作用。这样代码复用时,完全不需要考虑它的内部实现和外部影响
  • 更优雅的组合:往大的说,网页是由各个组件组成的。往小的说,一个函数也可能是由多个小函数组成的。更强的复用性,带来更强大的组合性

缺点:

  • 性能:函数式编程相对于指令式编程,性能绝对是一个短板,因为它往往会对一个方法进行过度包装,从而产生上下文切换的性能开销
  • 资源占用:在 JS 中为了实现对象状态的不可变,往往会创建新的对象,因此,它对垃圾回收所产生的压力远远超过其他编程方式
  • 递归陷阱:在函数式编程中,为了实现迭代,通常会采用递归操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太阳与星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值