JS函数式编程初探

100 阅读3分钟

1. 了解函数式编程

  • 函数式编程起源于20世纪30年代发明的lambda算法(lambda calculusλ-calculus)。
  • 在函数式编程中,函数可以发给函数,用作参数,也可以当做结果由函数返回。
  • 更为复杂的函数可以称为高阶函数(higher-order function),可以处理函数,把函数当做参数或返回结果,或者二者兼具。
  • 20世纪50年代,John McCarthy在λ演算的基础上发明了Lisp编程语言。Lisp实现了高阶函数的概念,把函数当做一等成员(或称一等公民)
  • 如果函数可以声明为变量,作为参数发给函数,那么这个函数就是一等成员。
  • JS支持函数式编程,因为在JS中,函数是一等成员。这意味着,变量可以做到的事,函数都能做到。
  • ES6新增的箭头函数,promise和展开运算符等使得函数式编程更加方便了。

2. 命令式编程和声明式编程

函数式编程是一个更大的编程范式的一部分,即声明式编程(declarative programming)。声明式编程是一种编程风格,采用声明式编程风格开发的应用有个显著特点:重点描述该做什么,而不管怎么做。与声明式编程对应的命令式编程风格则只关注如何使用代码得到结果。

下面这个例子展示了两种编程风格的不同:

  • 使用命令式编程实现把字符串中的空格替换成连字符
// 命令式编程方式处理URL字符串
const urlString = "Hello World & Special!";
let formattedUrl = "";

// 遍历字符串的每个字符
for (let i = 0; i < urlString.length; i++) {
    let currentChar = urlString[i];
    
    // 如果是空格,替换为连字符
    if (currentChar === " ") {
        formattedUrl += "-";
        continue;
    }
    
    // 跳过特殊字符
    if ("!?&=".includes(currentChar)) {
        continue;
    }
    
    // 将字符转换为小写并添加到结果中
    formattedUrl += currentChar.toLowerCase();
}

console.log(formattedUrl); // 输出: "hello-world"


  • 使用声明式编程改写上边的代码:
// 声明式编程方式处理URL字符串
const formatUrl = (url) => 
    url
        .split('')
        .map(char => char === ' ' ? '-' : char)
        .filter(char => !'!?&='.includes(char))
        .join('')
        .toLowerCase();
// 测试用例
console.log(formatUrl("My Profile Page!")); // 输出: "my-profile-page"
console.log(formatUrl("Hello World & Special!")); // 输出: "hello-world"

下面这个例子向我们展示了采用声明式编程风格的程序更易于理解,因为代码自身就说明了在做什么。

const loadAndMapMembers = compose(
    combineWith(sessionStorage, "members"),
    save(sesionStorage, "members"),
    scopeMembers(window),
    logMemberInfoConsole,
    logFieldsToConsole("name.first"),
    countMembersBy("location.state"),
    prepStatesForMapping,
    save(sessionStorage,"map"),
    renderUSMap
);

2. 对函数是一等公民的理解

2.1 基础用法

  • 函数可以使用var、let或const关键字声明,就像声明字符串、数字等变量一样。
var log = function(message){
    console.log(message)
}

log("在JS中,函数是变量,可以通过var、let或const定义")
  • 箭头函数的形式
const log = message => {
    console.log(message)
}
  • 函数用作变量
const obj = {
    message:"函数可以像变量一样添加到对象中",
    log(message){
        console.log(message)
    };
};
obj.log(obj.message);
  • 函数可以添加到JS数组中
const message = [
    "函数可以放在数组中",
    message => console.log(message),
    "就像变量一样",
]
message[1](message[0]) // 函数可以放在数组中

2.2 高阶用法

  • 函数可以像常规变量一样作为参数发给其他函数
/**
 * 定义一个高阶函数 insideFn,它接收一个 logger 函数作为参数
 * @params {function} logger
 */
const insideFn = logger => {
    logger("函数可以作为参数被传递给其他函数")
}
insideFn(message => console.log(message));
  • 函数可以像变量一样由其他函数返回
/**
 * 定义一个高阶函数 createScream,接收一个 logger 函数作为参数,
 * 返回一个新函数(scream),这个新函数接收一个 message 参数,
 * @param {function} logger 
 * @returns 
 */
const createScream = function(logger){
    return function(message){
        logger(message.toUpperCase() + "!!!");
    };
};
const scream = createScream(message => console.log(message));