JS函数式编程实现XDM一

盲猜一个:如果你有看过

《medium 五万赞好文-《我永远不懂 JS 闭包》》

《“类”设计模式和“原型”设计模式——“复制”和“委托”的差异》

这两篇文章,你一定会对 JS 的【函数】有更多兴趣!

皮一下,很舒服~ 没错!JS 就是轻量级的函数式编程!

拆解一下这句话,品味一下~

本瓜将借助《JavaScript 轻量级函数式编程》一书带领你先透析它的落脚点函数式编程,然后再看看 JS 为什么被称为是 “轻量的”!

此篇是《JS如何函数式编程》系列的第一篇,点赞关注持续追踪!

FP 概览

重要性

函数式编程(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 敏感的函数。这不需要理由。

以上就是JS函数式编程实现XDM一的详细内容,更多关于JS函数式编程XDM的资料请关注我们其它相关文章!

(0)

相关推荐

  • JS中的一些常用的函数式编程术语

    组合 Composition 组合某种类型(含函数)的两个元素,进而生成一个该类型的新元素: JavaScript let compose = (f, g) => a => f(g(a)) let toUpperCase = x => x.toUpperCase() let exclaim = x => x + '!' let shout = compose(exclaim, toUpperCase); shout("hello world") // HELLO

  • javascript函数式编程基础

    目录 一.引言 二.什么是函数式编程 三.纯函数(函数式编程的基石,无副作用的函数) 四.函数柯里化 五.函数组合 六.声明式和命令式代码 七.Point Free 八.示例应用 九.总结 一.引言 函数式编程的历史已经很悠久了,但是最近几年却频繁的出现在大众的视野,很多不支持函数式编程的语言也在积极加入闭包,匿名函数等非常典型的函数式编程特性.大量的前端框架也标榜自己使用了函数式编程的特性,好像一旦跟函数式编程沾边,就很高大上一样,而且还有一些专门针对函数式编程的框架和库,比如:RxJS.cy

  • JavaScript函数式编程(Functional Programming)组合函数(Composition)用法分析

    本文实例讲述了JavaScript函数式编程(Functional Programming)组合函数(Composition)用法.分享给大家供大家参考,具体如下: 组合(Composition)函数,就是把两个或以上的函数组合到一块儿,整成一个新的函数.我找到了一个很好的例子,很好地解释了组合函数这个概念. 比如一个应用主要是记录一下日常的花销(expenses),应用里的数据看起来像这样: const expenses = [ { name: '租金', price: 3000, type:

  • JavaScript中的函数式编程详解

    函数式编程 函数式编程是一种编程范式,是一种构建计算机程序结构和元素的风格,它把计算看作是对数学函数的评估,避免了状态的变化和数据的可变,与函数式编程相对的是命令式编程.我们有这样一个需求,给数组的每个数字加一: // 数组每个数字加一, 命令式编程 let arr = [1, 2, 3, 4]; let newArr = []; for(let i = 0; i < arr.length; i++){ newArr.push(arr[i] + 1); } console.log(newArr)

  • JS函数式编程实现XDM一

    盲猜一个:如果你有看过 <medium 五万赞好文-<我永远不懂 JS 闭包>> <“类”设计模式和“原型”设计模式——“复制”和“委托”的差异> 这两篇文章,你一定会对 JS 的[函数]有更多兴趣! 皮一下,很舒服~ 没错!JS 就是轻量级的函数式编程! 拆解一下这句话,品味一下~ 本瓜将借助<JavaScript 轻量级函数式编程>一书带领你先透析它的落脚点函数式编程,然后再看看 JS 为什么被称为是 “轻量的”! 此篇是<JS如何函数式编程>

  • JS轻量级函数式编程实现XDM三

    目录 前言 组合函数 含义 封装盒子 任意组合 compose 变体 抽象能力 阶段小结 前言 这是[JS如何函数式编程]系列文章第三篇.点赞关注,持续追踪 前两篇传送门: <XDM,JS如何函数式编程?看这就够了!(一)> <XDM,JS如何函数式编程?看这就够了!(二)> 在第二篇,我们谈了基础之基础,重要之重要——“偏函数”,偏函数通过函数封装,实现了减少传参数量的目的,解决了手动指定实参的麻烦. 更具重要意义的是: 当函数只有一个形参时,我们能够比较容易地组合它们.这种单元

  • JS轻量级函数式编程实现XDM二

    目录 前言 偏函数 传参现状 封装 partial 运行机制 拓展 partial 柯里化 阶段小结 前言 承接上一篇<XDM,JS如何函数式编程?看这就够了!(一)>,我们知道了函数式编程的几个基本概念. 这里作简要回顾: 函数式编程目的是为了数据流更加明显,从而代码更具可读性: 函数需要一个或多个输入(理想情况下只需一个!)和一个输出,输入输出是显式的代码将更好阅读: 闭包是高阶函数的基础: 警惕匿名函数: 弃用 this 指向: 本篇将着重介绍第 2 点中函数的输入,它是 JS 轻量函数

  • JS函数式编程之纯函数、柯里化以及组合函数

    目录 前言 纯函数 纯函数的概念 副作用 纯函数案例 柯里化 柯里化的概念 函数柯里化的过程 函数柯里化的特点及应用 自动柯里化函数的实现 组合函数 前言 函数式编程(Functional Programming),又称为泛函编程,是一种编程范式. 早在很久以前就提出了函数式编程这个概念了,而后面一直长期被面向对象编程所统治着,最近几年函数式编程又回到了大家的视野中,JavaScript是一门以函数为第一公民的语言,必定是支持这一种编程范式的. 下面就来谈谈JavaScript函数式编程中的核心

  • js 函数式编程学习笔记

    (1)平常写的函数大多是接受值,合并值,返回值,比如经常写的for循环: function printArray(array){ for(var i=0;i<array.length;i++){ print(array[i]); } } 但是如果我们想做print之外的事情呢?怎么办?再写一个相似的,未免显得浪费,我们可以这样 function forEach(array,action){ for(var i=0;i<array.length;i++){ action(array[i]); }

  • JavaScript函数式编程Thunk原理解析

    目录 引言 举个栗子 代码演进 改造成异步 toThunk 工具函数 引言 本篇带来 Thunk 理解,这也是本瓜最津津乐道的 JS 函数式编程中延迟处理的思想核心之一! 什么是 Thunk ? 简单理解:在计算机编程中,Thunk 就是一种实现延迟执行的手段. 举个栗子 我要计算 99 的 9 次方,然后再把它打印出来,你会怎么写? 大聪明肯定是直接一句话: console.log(Math.pow(99, 9)) // 913517247483640800 有点想法的同学肯定想封装一个函数,

  • js面向对象编程OOP及函数式编程FP区别

    目录 写在前面 javscript 中函数和对象的关系 面向对象编程(OOP) 继承 多态 封装 函数编程编程(FP) 闭包和高阶函数 柯里化 偏函数 组合和管道 函子 写在最后 写在前面 浏览下文我觉得还是要有些基础的!下文涉及的知识点太多,基本上每一个拿出来都能写几篇文章,我在写文章的过程中只是做了简单的实现,我只是提供了一个思路,更多的细节还是需要自己去钻研的,文章内容也不少,辛苦,如果有其他的看法或者意见,欢迎指点,最后纸上得来终觉浅,绝知此事要躬行 javscript 中函数和对象的关

  • 《JavaScript函数式编程》读后感

    本文章记录本人在学习 函数式 中理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习. 在近期看到了<JavaScript函数式编程>这本书预售的时候就定了下来.主要目的是个人目前还是不理解什么是函数式编程.在自己学习的过程中一直听到身边的人说面向过程编程和面向对象编程,而函数式就非常少.为了自己不要落后于其他同学的脚步,故想以写笔记的方式去分享和记录自己阅读中所汲取的知识. js 和函数式编程 书中用了一句简单的话来回答了什么是函数式编程: 函数式编程通过使用函数来将值转换为抽象单元

  • JavaScript的函数式编程基础指南

    引言 JavaScript是一种强大的,却被误解的编程语言.一些人喜欢说它是一个面向对象的编程语言,或者它是一个函数式编程语言.另外一些人喜欢说,它不是一个面向对象的编程语言,或者它不是一个函数式编程语言.还有人认为它兼具面向对象语言和函数式语言的特点,或者,认为它既不是面向对象的也不是函数式的,好吧,让我们先搁置那些争论. 让我们假设我们共有这样的一个使命:在JavaScript语言所允许的范围内,尽可能多的使用函数式编程的原则来编写程序. 首先,我们需要清理下脑子里那些关于函数式编程的错误观

随机推荐