1. 描述

函数化编程是指: 当我们需要写一个功能函数时, 不要将大量的逻辑写在同一个函数中, 而是将这个功能块分割成为一个个小的功能模块, 最后通过函数复用来完成目的.

2. demo

2.1 场景描述

现在有如下对象:

let obj = {
    'oulae_key': {
        'ming_key': 'oulae'
    }
}

需求: 将对象中的key 转换为驼峰命名法

2.2 直观解决方案

直观上, 遇到这种问题的就是一把梭, 把所有的逻辑都写到一个函数中

const log = console.log.bind(console)

const isObject = s => Object.prototype.toString.call(s) === '[object Object]'

const dealFuncKey = (obj) => {
    let result = {}
    Object.keys(obj).forEach((key) => {
        let afterKey = ''
        for (let i = 0, len = key.length; i < len; i++) {
            let temp = key[i]
            if (temp === '_') {
                continue
            } else if (key[i - 1] === '_') {
                afterKey += key[i].toUpperCase()
            } else {
                afterKey += temp
            }
        }
        // result[afterKey] = obj[key]
        result[afterKey] = isObject(obj[key]) ? dealFuncKey(obj[key]) : obj[key]
    })
    return result
}

const __main = () => {
    let obj = {
        'oulae_key': {
            'ming_key': 'oulae'
        }
    }
    log(dealFuncKey(obj))
}

__main()

2.3 函数化的解决方案

const log = console.log.bind(console)

const isObject = s => Object.prototype.toString.call(s) === '[object Object]'

// 将含有_字符的str 转换为驼峰命名方式
const transfromKey = (str) => {
    let result = ''
    for (let i = 0, len = str.length; i < len; i++) {
        let temp = str[i]
        if (temp === '_') {
            continue
        } else if (str[i - 1] === '_') {
            result += str[i].toUpperCase()
        } else {
            result += temp
        }
    }
    return result
}

// 处理Object 主函数
const dealFuncKey = (obj) => {
    let result = {}
    Object.keys(obj).forEach((key) => {
        let afterKey = transfromKey(key)
        // result[afterKey] = obj[key]
        result[afterKey] = isObject(obj[key]) ? dealFuncKey(obj[key]) : obj[key]
    })
    return result
}

const __main = () => {
    let obj = {
        'oulae_key': {
            'ming_key': 'oulae'
        }
    }
    log(dealFuncKey(obj))
}

__main()

3. 两种方案的比较

在传统方案中, 我们选择把逻辑封装在一起完成, 没有进行逻辑抽离;

在函数化编程解决方案中, 我们尽量做到一个函数只维持一个功能, 然后通过函数的调用完成逻辑组装.

如果对于个人写demo, 传统的解决方案当然更方面, 你只需要按照自己的思路逻辑一直往后写就行, debug 时也不需要跳转到其他函数;

但是, 这种方案存在着一个非常大的弊端, 如果你面临一个十分复杂的函数, 要处理的情况非常冗杂, 那么用传统方案也许可以完成目标, 但是它会造成一些非常严重的后果:

  1. 代码复用率低, 很多场景功能函数没法得到复用, 比如比较对象, 判断字符串
  2. 理解困难, 如果你代码非常冗杂, 当项目发生变更或者项目交接时, 就需要再次理解代码, 这种写法理解成本非常高, 因为你需要从头到头摸清楚所有的细节
  3. 代码改动困难, 想象一下, 如果项目逻辑发生变更, 那么你改动一个100行甚至1000行的函数, 你会奔溃吗?(这完全是有可能的)
  4. 如果你写的函数足够复杂, 那么你必须一次性把该内容写完, 如果被打断, 你的思路就可能被破坏了.
  5. 面向对象编程, 一个对象只做一件事, 如果他要做其他事情那么通过组合, 继承的方式实现

所以, 虽然函数化编程虽然在第一次写时对于开发者逻辑抽离要求比较高, 同时debug 相较于传统方案比较繁琐, 但是对于复杂的逻辑以及整个项目的生命周求还是非常有好处的

如果在一个项目, 我们使用函数化编程来处理数据, 每个函数只有一个功能, 然后通过函数调用去组织功能; 它在以下的方面是非常有优势的:

  1. 可读性: 函数化编程, 每个函数都只是一个功能, 如果知道功能之后, 完全可以忽视函数的实现细节
  2. 内容更改: 函数化编程, 首先就将数据互相隔离了, 如果你需要更改数据, 只要保证函数的返回结果符合要求即可; 而且如果完全遵循复用的原则, 那么代码改动将会非常少.
  3. 思路可视化: 函数的应用调用过程, 就是编写的逻辑思考过程.