百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

前端必学——函数式编程(一)_前端函数库

myzbx 2025-09-04 13:57 13 浏览


重要性

函数式编程(FP),不是一个新的概念,它几乎贯穿了整个编程史。直到最近几年,函数式编程才成为整个开发界的主流观念。

函数式编程有完善且清晰的原则,一旦我们知道这些原则,我们将能更加快速地读懂代码,定位问题。这是为什么函数式编程重要的原因!

比如:你可能写过一些命令式的代码,像 if 语句和 for 循环这样的语句。这些语句旨在精确地指导计算机如何完成一件事情。而声明式代码,以及我们努力遵循函数式编程原则所写出的代码,更专注于描述最终的结果。

函数式编程以另一种方式来思考代码应该如何组织才能使数据流更加明显,并能让读者很快理解你的思想。

记住,你编写的每一行代码之后都要有人来维护,这个人可能是你的团队成员,也可能是未来的你。

最初的函数

函数式编程不是仅仅用 function 这个关键词来编程,就像面向对象编程不仅仅是用了对象就算是。

函数的真正意义是什么?

回到最初的起点,我们心中的函数一定是这样的:

f(x) = 2x2 + 3,这是数学上真正的函数。那这和函数式编程有什么关系呢?

函数的本质是【映射】。以一个优雅的方式来描述一组值和另一组值的映射关系,即函数的输入值与输出值之间的关联关系。

在编程中,它或许有许多个输入值,或许没有。它或许有一个输出值( return 值),或许没有。

如果你计划使用函数式编程,你应该尽可能多地使用函数,而不是程序。你所有编写的 function 应该接收输入值,并且返回输出值。(这么做的原因是多方面的,后续会一一介绍)

这里,输入值就是函数传参,输出值就是return的东西。(如果你没有 return 值,或者你使用 return;,那么则会隐式地返回 undefined 值。)

思考“return”

  • 思考以下代码:
function foo(x) {
    if (x > 10) return x + 1;

    var y = x / 2;

    if (y > 3) {
        if (x % 2 == 0) return x;
    }

    if (y > 1) return y;

    return x;
}

foo(2) 返回什么? foo(4) 返回什么? foo(8), foo(12) 呢?

  • 将上述代码变形,请思考:
function foo(x) {
    var retValue;

    if (retValue == undefined && x > 10) {
        retValue = x + 1;
    }

    var y = x / 2;

    if (y > 3) {
        if (retValue == undefined && x % 2 == 0) {
            retValue = x;
        }
    }

    if (retValue == undefined && y > 1) {
        retValue = y;
    }

    if (retValue == undefined) {
        retValue = x;
    }

    return retValue;
}

这样写会更容易理解吗?

在每个 retValue 可以被设置的分支, 这里都有个守护者以确保 retValue 没有被设置过才执行。(?)

相比在函数中提早使用 return,我们更应该用常用的流控制( if 逻辑 )来控制 retValue 的赋值。到最后,我们 return retValue。

用 return 来实现流控制,会创造更多的隐含意义。

  • 再来思考以下代码:
// 通过一个函数修改变量 y 的值
var y;

function foo(x) {
    y = (2 * Math.pow( x, 2 )) + 3;
}

foo( 2 );

y;    

但是我们也可以这样写:

function foo(x) {
    return (2 * Math.pow( x, 2 )) + 3;
}

var y = foo( 2 );

y; 

后一个版本更有优势吗?

答案是肯定的:有!

后一个版本中的 return 表示一个显式输出,而前者的 y 赋值是一个隐式输出。

通常,开发人员喜欢显式模式而不是隐式模式。

为什么说后者 return 出来的就是显式的?而前者的 y 赋值是隐式的?

这个例子可以给你答案:

function sum(list) {
    var total = 0;
    for (let i = 0; i < list.length; i++) {
        if (!list[i]) list[i] = 0; // list 使用了 nums 的引用,不是对 [1,3,9,..] 的值复制,而是引用复制。

        total = total + list[i];
    }

    return total;
}

var nums = [ 1, 3, 9, 27, , 84 ];

sum( nums );            // 124

这段代码,除了 return 的输出,还有没有其它输出可能改变到函数外部参数 nums 的值?

是有的!就是在注释的一行,我们无意中改变了 nums 。

console.log(nums) // [1, 3, 9, 27, 0, 84]

JS 对数组、对象和函数都使用引用和引用复制,我们可以很容易地从函数中创建输出,即使是无心的。

这个隐式函数输出在函数式编程中有一个特殊的名称:副作用。

没有副作用的函数也有一个特殊的名称:纯函数,这个概念十分重要,后面对有更多讨论!

高阶函数

一个函数如果可以接受或返回一个甚至多个函数,它被叫做高阶函数。

其中最强大的就是:【闭包】。

我们将在的后续举例中大量使用闭包。它可能是所有函数式编程中最重要的基础。

此处举一小例:

假设你需要将两个值相加,一个你已经知道,另一个还需要后面才能知道,你可以使用闭包来记录第一个输入值:

function makeAdder(x) {
    return function sum(y){
        return x + y;
    };
}

//我们已经分别知道作为第一个输入的 10 和 37
var addTo10 = makeAdder( 10 );
var addTo37 = makeAdder( 37 );

// 紧接着,我们指定第二个参数
addTo10( 3 );            // 13
addTo10( 90 );            // 100

addTo37( 13 );            // 50

这种在连续函数调用中指定输入,是函数式编程中非常普遍的形式。

它可分为两类:偏函数应用柯里化。后续会展开。

具名函数

我们提倡要用具名函数,而不是匿名函数,这更有利于我们语义化代码,比如getPreferredName(..),操作意图很明确,并且可以很好的回溯问题,防止出现 (anonymous function)

但是 => 箭头函数除外,箭头函数还是得有效利用。

=> 箭头函数令人兴奋的地方在于它几乎完全遵循函数的数学符号,特别是像 Haskell 这样的函数式编程语言。它能简化、优化代码片段中的空间。

丢掉“this"

JavaScript 中的 this 绑定规则是真的难记,好消息是我们将把 this 丢弃掉,不去理会它。

这样做的内核原因是:this 是函数的一个隐式的输入参数。前面我们提到通常,开发人员喜欢显式模式而不是隐式模式。,这样的隐式输入违背了我们的原则。

阶段小结

函数是强大的!

我们学习函数式编程的全部理由是为了书写更具可读性的代码。

程序中,函数不仅仅是一个语句或者操作的集合,而是需要一个或多个输入(理想情况下只需一个!)和一个输出。开发人员喜欢显式输入输出而不是隐式输入输出。

函数内部的函数可以取到闭包外部变量,并记住它们以备日后使用。这是所有程序设计中最重要的概念之一,也是函数式编程的基础。

要警惕匿名函数,特别是 => 箭头函数。虽然在编程时用起来很方便,但是会对增加代码阅读的负担。

别用 this 敏感的函数。这不需要理由。

我是掘金安东尼: 一名人气前端技术博主(文章 100w+ 阅读量)

终身写作者(INFP 写作人格)

坚持与热爱(简书打卡 1000 日)

我能陪你一起度过漫长技术岁月吗(以梦为马)

觉得不错,给个点赞和关注吧(这是我最大的动力 )b( ̄▽ ̄)d

相关推荐

微信又双叒叕更新了!这次是安卓版

澎湃新闻综合报道近日安卓版微信正式更新了8.0.10版主要有四大更新日常使用起来会更加方便一起来看看吧1朋友圈视频封面在此之前,朋友圈背景一直只能放静态图片,但此次更新后,可以从视频号中选择一段...

镜子里的你和照片里的你,哪个更真实?

不知道大家有没有这样的经历。聚餐、团建……一群人拍合照,拍完之后,我们满心期待地放大照片,却惊慌失措地发现——怎么自己又被拍得这么丑!但这时,别人总是会说道——「这就是你平常的样子啊。」可是,我们平时...

歼20战斗机现身珠海,首次公开静态展示,体现解放军的自信和强大

日本航空自卫队在9月份举行了三泽基地开发日活动,期间出动12架F-35A闪电II战斗机进行了公开展示,不过仅仅是编队通场飞过而已。日本航空自卫队仅仅动用1架F-35A战斗机进行了机动飞行表演,从公开的...

Java类初始化阶段深度解析:执行顺序与线程安全

一、初始化阶段核心机制二、分步详解与代码验证1.初始化触发条件主动使用场景:publicclassInitTrigger{static{System.out.pr...

深入剖析 Java 类加载机制:原理、优化与实践

作为Java开发者,你是否遇到过这样的场景:线上服务突然抛出NoClassDefFoundError,但本地调试却一切正常;或者明明引入了依赖JAR,却始终报ClassNotFoundExcep...

SUID/SGID是啥?如何让普通用户拥有root的能力?

原文链接:「链接」在Linux系统中,权限控制是一项至关重要的安全机制。除了常见的r(读)、w(写)和x(执行)权限外,还有三种特殊权限位常被忽视:SUID(SetUserID)、SGID...

数码宝贝新世纪:SP奥米加兽AS情报泄露,是否也是强力辅助?

大家好!我是小飉[liáo],欢迎来阅!情怀手游《数码宝贝新世纪》官方不按套路出牌,这次公布的入围测试的人员名单,但是并没有公布SP奥米加兽AS的能力情报,还好广大网友给力。次日,在论坛,以及...

抽象类(abstract class)与接口(interface)

A.核心概念1.抽象类-定义:带有abstract修饰符的类,不能被实例化,用于定义一组方法签名和可选的部分公共实现。-特性:-可以包含字段、构造函数、已实现的方法(带方法体)和抽象方法(...

S39结束时间确定,新赛季段位继承公布,大量皮肤在7月初集体上线

文/静海君如果说之前都还是猜测的话,那游戏内的一个变动,基本100%确定了新赛季(S40)的开启时间。新赛季的开启时间关于新赛季的开启时间,目前主要有两个线索。第一个关于新赛季开启时间的线索是「游戏内...

一篇文章掌握整个JVM,JVM超详细解析!!!

不懂JVM看完这一篇文章你就会非常懂了,文章很长,非常详细!!!先想想一些问题1我们开发人员编写的Java代码是怎么让电脑认识的首先先了解电脑是二进制的系统,他只认识01010101比如我们经常要...

项目用 JDK17 后,bug 少了、速度快了!这 4 个好处太实在

别再死守JDK8了!去年把电商项目升级到JDK17,团队直接爽翻:代码量少写1/3,大促再也不卡顿,运维半夜不call人,连测试都夸bug少了。今天就说真话,JDK17在项目里的4...

法定继承有顺序:在法定继承人中,谁应该优先继承?

免费问律师_法律咨询免费24小时律师在线解答-法临网“父母去世没留遗嘱,兄弟姐妹争遗产闹上法庭!”法定继承中,谁优先拿财产?《民法典》明确“顺序+份额”规则,一文说清关键点,避免家庭内耗!一、法定...

前端必会:ES5寄生继承 vs ES6 Class继承

大家好,我是谦!说到继承,估计不少前端开发者都踩过坑。尤其是在ES5到ES6的过渡阶段,我们写代码时常常被问到:“你用的是原型继承还是Class继承?”再加上面试官特别喜欢追问底层实现——...

子女入了外籍能否继承父母国内的房产呢?

大家好,这里是家理范律,专注遗产继承、婚姻家事领域!-很多加入外籍的朋友都纠结:自己还能继承国内父母的房产吗?答案是可以继承,但流程远比想象复杂!-真实案例:美籍华人张先生,拿着父母在加州公证的遗嘱回...

J.A.C.S | 基于化学类型和靶点的基因组挖掘以寻找一种新的细菌肽脱甲酰酶天然产物抑制剂

大家好,今天推送的文章是2025年6月发表在JournaloftheAmericanChemicalSociety上的“Chemotype-andTarget-DrivenGenome...