JS基础⚡️ 解析函数式编程

介绍

JavaScript中的函数式编程是一种编程范式,它将函数视为一等公民,并强调使用纯函数、不可变数据和函数组合来构建程序。函数式编程在JavaScript中得到了广泛应用,特别是在处理异步操作、函数组合和数据转换等方面。

React 和 Vue 3 都在向函数式编程靠拢,尤其是在最新的版本中,它们都引入了一些函数式编程的概念和特性。

React 在 Hooks 的引入中,采用了函数组件作为主要的组件形式,这使得组件可以更加纯粹地关注状态和渲染。Hooks 提供了一系列的函数,如 useStateuseEffectuseMemo 等,使得状态管理和副作用处理更加简洁和可组合。这些函数可以帮助开发者遵循函数式编程的原则,例如使用纯函数来处理状态更新,避免共享状态和可变数据等。

Vue 3 在 Composition API 的引入中,也采用了函数式的风格。Composition API 允许开发者将逻辑按照功能进行组合,而不是按照生命周期钩子进行组织。这样可以更好地重用逻辑和提高代码的可读性。Composition API 还提供了一些函数,如 reactivecomputedwatch 等,用于处理响应式数据和副作用。这些函数使得状态管理和副作用处理更加灵活和可控。

并且函数作为JavaScript的一等公民,这意味着函数可以像其他数据类型一样进行操作和传递。

JS 复制代码
//1.函数可以被赋值给变量
const greet = function (name) {
  console.log(`Hello, ${name}!`)
}
greet('Alice') // 输出 "Hello, Alice!"
//2函数可以作为参数传递给其他函数: 
function sayHello(greetingFunction) {
    greetingFunction("Alice");
  }
  
  function greet(name) {
    console.log(`Hello, ${name}!`);
  }
  
sayHello(greet); // 输出 "Hello, Alice!
//3 函数可以作为其他函数的返回值
function createMultiplier(multiplier) {
    return function(number) {
      return number * multiplier;
    };
  }
  
  const double = createMultiplier(2);
  console.log(double(5)); // 输出 10
//4.函数可以存储在对象中:
const person = {
    name: "Alice",
    greet: function() {
      console.log(`Hello, ${this.name}!`);
    }
  };
  
person.greet(); // 输出 "Hello, Alice!"
//这种灵活性使得函数可以更加灵活地使用和组合,
//从而实现更好的代码可读性、可维护性和可重用性。

接下来让我们学习一下函数式编程吧

JavaScript 中函数式编程的主要特点

1.纯函数

纯函数是指在相同的输入下,返回相同的输出,并且没有副作用的函数。纯函数不依赖于外部状态,使得代码更加可靠、可测试和可维护。

js 复制代码
//纯函数应该满足以下条件:
//相同的输入总是产生相同的输出。
//函数执行过程中不会改变外部状态。
//函数没有产生可观察的副作用
function add(a, b) {
  return a + b
}  //是纯函数

function test(a,b){
console.log("a")
} //不是纯函数 有可观察的副作用

function HelloWorld(props) {
    props.info = {}
    props.info.name = 'why'
} //不是纯函数 直接修改了`props`对象的属性

//修改为纯函数
 function HelloWorld(props) {
    const newProps = { ...props };
    newProps.info = {};
    newProps.info.name = 'ZhaiMou';
    return newProps;
 }
  1. 不可变性:函数式编程中的数据结构是强调不可变的,即一旦创建就不能再被修改。这样做的好处是避免了共享状态并发冲突,因为不需要对数据进行加锁,也可以提高程序的可靠性。
js 复制代码
const arr = [1, 2, 3];
// 使用concat()方法创建一个新的数组,而不是修改原始数组
const newArr = arr.concat(4);
const newArr1 = [...arr, 4]
console.log(arr);     // 输出: [1, 2, 3]
console.log(newArr);  // 输出: [1, 2, 3, 4]
console.log(newArr1);  // 输出: [1, 2, 3, 4]
const obj = { name: "Alice", age: 25 };
// 使用Object.assign()方法创建一个新的对象,而不是修改原始对象
const newObj = Object.assign({}, obj, { age: 30 });
console.log(obj);     // 输出: { name: "Alice", age: 25 }
console.log(newObj);  // 输出: { name: "Alice", age: 30 }
  1. 高阶函数:函数式编程中的函数可以作为参数传递给其他函数,也可以作为返回值返回给调用者。这样做的好处是可以提高代码的复用性和灵活性
js 复制代码
// 高阶函数示例
function multiplyBy(factor) {
    return function(number) {
      return number * factor;
    }
  }
  
const double = multiplyBy(2); // 创建一个乘以2的函数
console.log(double(5)); // 输出: 10

函数柯里化 ⭐⭐⭐⭐⭐

js 复制代码
// 原始函数
function add(a, b, c) {
  return a + b + c;
}
//柯里化函数
function curryAdd(a) {
    return function(b) {
      return function(c) {
        return a + b + c;
      };
    };
}
// 柯里化函数单一职责的原则
function sum(x) {
  x = x + 2

  return function(y) {
    y = y * 2

    return function(z) {
      z = z * z

      return x + y + z
    }
  }
}
console.log(sum(10)(20)(30))
// 柯里化函数的实现hyCurrying
function hyCurrying(fn) {
  function curried(...args) {
    // 判断当前已经接收的参数的个数, 可以参数本身需要接受的参数是否已经一致了
    // 1.当已经传入的参数 大于等于 需要的参数时, 就执行函数
    // 函数的length为参数的个数 fn.length
    if (args.length >= fn.length) {
      // fn(...args)
      // fn.call(this, ...args)
      return fn.apply(this, args)
    } else {
      // 没有达到个数时, 需要返回一个新的函数, 继续来接收的参数
      function curried2(...args2) {
        // 接收到参数后, 需要递归调用curried来检查函数的个数是否达到
        return curried.apply(this, args.concat(args2))
      }
      return curried2
    }
  }
  return curried
}

let curryAdd = hyCurrying(add1) 
console.log(curryAdd(10, 20, 30)) //60
console.log(curryAdd(10, 20)(30)) //60
console.log(curryAdd(10)(20)(30)) //60
  1. 函数组合:函数组合是指将多个函数组合成一个新函数。这种特点可以让代码更简洁、高效和可读性更强。
js 复制代码
// 函数组合示例
function addOne(number) {
    return number + 1;
  }
  
  function multiplyByTwo(number) {
    return number * 2;
  }
  
  function compose(func1, func2) {
    return function(arg) {
      return func1(func2(arg));
    };
  }
  
  const addOneAndMultiplyByTwo = compose(multiplyByTwo, addOne); // 组合函数
  console.log(addOneAndMultiplyByTwo(3)); // 输出: 8

通用组合函数实现

js 复制代码
function hyCompose(...fns) {
  let length = fns.length
  for (let i = 0; i < length; i++) {
    if (typeof fns[i] !== 'function') {
      throw new TypeError('Expected arguments are functions')
    }
  }

  function compose(...args) {
    let index = 0
    let result = length ? fns[index].apply(this, args) : args
    while (++index < length) {
      result = fns[index].call(this, result)
    }
    return result
  }
  return compose
}
function double(m) {
  return m * 2
}

function square(n) {
  return n * 2
}

var newFn = hyCompose(double, square)
console.log(newFn(10)) //40
  1. 函数式编程支持延迟计算,也就是推迟表达式的求值直到需要的时候。这种方式可以减少不必要的计算,并提高性能。例如,JavaScript中的lazy evaluation特性允许我们使用生成器、迭代器和惰性操作来实现延迟计算。
js 复制代码
// 延迟计算示例
function* generateNumbers() { // 定义生成器函数
    let number = 0;
    while (true) {
      yield number++;
    }
  }

  const numbers = generateNumbers(); // 创建一个生成器对象
  console.log(numbers.next().value); // 输出: 0
  console.log(numbers.next().value); // 输出: 1

由于生成器函数是惰性求值的,即只有在需要时才会计算值,因此它可以减少不必要的计算,并提高性能。常见的还有异步请求和惰性加载和防抖节流等等

  1. 数据转换和操作
js 复制代码
// 数据转换和操作示例
const numbers = [1, 2, 3, 4];

const doubledNumbers = numbers.map(num => num * 2); // 映射操作
console.log(doubledNumbers); // 输出: [2, 4, 6, 8]

const evenNumbers = numbers.filter(num => num % 2 === 0); // 过滤操作
console.log(evenNumbers); // 输出: [2, 4]

const sum = numbers.reduce((acc, curr) => acc + curr, 0); // 归约操作
console.log(sum); // 输出: 10

我们使用数组的mapfilterreduce方法对数字数组进行了转换和操作。我们可以使用这些操作以声明性的方式处理数据,而不需要显式的循环和状态管理。

文章更多作为自我学习,有错欢迎指出。

相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端