javascript中的函数式编程

     2022-03-14     250

关键词:

本文和大家分享的主要是javascript中函数式编程相关内容,一起来看看吧,希望对大家学习javascript有所帮助。
函数式编程(functional programming)或称函数程序设计,又称泛函编程,是一种编程范型,比起命令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。
函数式编程,近年来一直被炒得火热,国内外的开发者好像都在议论和提倡这种编程范式。在众多的函数式语言中,Javascript 无疑是最亮眼的一个,越来越多的人开始学习和拥抱它,并使用它运用函数式编程来开发实际的大型应用,开源社区也源源不断的诞生函数式风格的框架和类库(Angular / React / Redux)。
作为 web 平台唯一的标准通用语言,Javascript 在软件历史上掀起了最大的语言热潮,拥有当下最大的开源包管理工具(npm)的Javascript 也从 Lisp 手中接过了维持数十年的 最流行的函数式编程语言” 的名号。在Javascript 的世界中是天然支持函数式编程的,函数式编程的基本特征有:
· 一等函数
· 闭包
· 高阶函数
· 纯度
本文会以 Javascript 为例子,和大家一起来了解和学习函数式编程。
一等函数(First Class Functions)
一等函数这个术语最早在20世纪60年代,由英国计算机科学家 Christopher Strachey  functions as first-class citizens 一文中提出的。意思是指,函数和其他一等公民(Number / String...)一样,拥有和它们一样的能力和作用:
·函数储存为变量
const foo = () => {...}
·函数可以储存为数据的一个元素
const arr = [1, 2, () => {...}]
·函数可以作为对象的属性值
const obj = {name: ’xx’, say: () => {}}
·函数可以在使用时直接创建出来
1 + (() => { return 2; })()
·函数可以作为变量传递给另一个函数
bar (name, fun) { fun(name) }
bar(’xx’, (name) => { console.log(name) })
·函数可以被另一个函数返回
foo() {
return () => {...}
}
在函数式编程中,函数是作为基本单元,并且在函数之上建立代码和数据的封装,以提高应用的重用和灵活性。支持一等函数的作用是显而易见的,我们可以使用函数去完成大部分的功能。
闭包(Closure)
历经了 30年,闭包终于成为了编程语言的主要特点。但是根据一项调查显示,有关 Javascript 闭包的问题占了 23% 左右,对于相当数量的开发者来说闭包仍然模糊而又神秘。对于闭包解释我还是更倾向于 Kyle Simpson的系列书 You Don’t Know JavaScript 中的解释:
函数在被定义时是可以访问当前的词法作用域,当函数离开作用域之外被执行时,就形成了闭包。
简而言之,闭包就是一个函数,捕获了作用域内的外部绑定。来看个例子:
function student (people) {
return (name) => { return people[name] }
}var someone = student({xx: {age: 20}, jackson: {age: 21}})
someone(’xx’) // {age: 20}
在执行完 student 函数后,里面的匿名函数形成了一个闭包,闭包是可以访问到 people 对象。闭包为Javascript 提供了私有访问,这让给开发者建立数据抽象提供了极大地便利,也可以更好地书写函数式代码,建立更加强大的代码。
来思考一个场景,手头上拥有一个书本的数组,数组里面包含了书本的信息,现在需要做的是找出把书名填充到一个数组中并且返回,我们一般都会这样写:
const books = [{title: ’人类简史’, author: ’zz’}, {title: ’禅与摩托车维修艺术
books.map((item) => { return item.title })
我们使用了 Array.prototype.map 方法,传入了一个匿名函数,函数中 return 了书名 title。假如需要利用闭包来进一步抽象的话,要怎么写呢?
function plucker (key) {
return  (obj) => {
return (obj && obj[key])
}
}
books.map(plucker(’title’))
我们定义了一个 plucker 函数,它接收一个 key 参数并返回一个匿名函数,匿名函数就是一个闭包并补捕获了 key 参数。在利用了闭包的情况下,我们可以传入任意想要的书本信息(比如:plucker(’author’)),这样就提高了代码的重用性和灵活性。当我们对于闭包认识足够充分时并合理运用到实际开发中去,将会切身体会到闭包的威力和它给我们带来的便利。
高阶函数(Higher Order Functions)
在数学和计算机科学中,高阶函数式至少满足下列一个条件的函数:
· 接受一个或多个函数作为输入
· 输出一个函数
在上述的 plucker 函数就是一个例子,还有我们熟知的 Array.prototype 相关的方法,比如 .map.sort 等等都是高阶函数,因为它们满足接受一个函数作为参数的条件。
那么先来看一个一阶函数的例子,定义一个函数,它会将数组中4个字母的单词给过滤掉:
const words = [’foo’, ’bar’, ’test’, ’some’]; const filter = words => {
let arr = [];
for(let i = 0, { length } = words; i < length; i++) {
const word = word;
if(word.length !== 4) {
arr.push(word);
}
}
return arr;
}
filter(words); // [’foo’, ’bar’]
假如现在又需要过滤数组中,以 ‘b’ 字母开头的单词?那么再定义一个函数:
const startWith = words => {
let arr = [];
for(let i = 0, { length } = arr; i < length; i++) {
const word = word;
if(word.indexOf(’b’) !== 0) {
arr.push(word);
}
}
return arr;
}
filter(words); // [’foo’, ’test’, ’some’]
根据上面两个函数的对比来看,其实主要代码的逻辑都是相似的,先遍历数组再进行条件判断,最后 push到数组中。其实,遍历和过滤都可以抽象出来,可以方便其他的类似函数去调用,毕竟在数组中根据条件过滤是很常见的需求。
const reduce = (reducer, init, arr) => {
let acc = init;
for(let i = 0,{ length } = arr; i < length; i++) {
acc = reducer(acc, arr);
}
return acc;
}
reduce((acc, curr) => acc + curr, 0, [1, 2, 3]);    // 6
如果使用过 Underscore 库的话,就会发现 reduce 和 Underscore.reduce 作用是一样的,实现的是累计的功能。reduce 接受了 3个参数:ruducer 函数、累计的初始值和一个数组,遍历时将每个数组元素作为 reducer 的参数传入,返回值又赋值给累计变量 init,遍历完成时也就完成了累计的功能。
现在如果将 rudece 应用到第一个需求上(过滤四个字母的单词):
const func = (fn ,arr) => {
return reduce((acc, curr) => fn(curr) ? acc.concat([curr]) : acc, [], arr)
}console.log(func(word => word.length !== 4, words)); // ["foo", "bar"]
可以发现,将公共代码抽象出来之后,filter 的函数实现非常简洁,只需传入不同的条件函数,就能为我们去处理符合各种条件的数据。高阶函数可以用来实现函数的多态性,并且相对于一阶函数,高阶函数的复用性和灵活性更好。
纯度(Purity)
函数式编程不仅仅只关心函数,也是思考如何尽量地降低软件复杂性的一种方式。在一些函数式编程语言中,纯度是被强制执行的,不允许使用有副作用的表达式。但是在 Javascript 中,纯度必须通过管理区实现,并且非常容易在偶然间创建和使用非纯函数。
一个纯函数需要满足以下三个条件:
· 函数结果只能通过参数来计算得出
· 不能依赖于能被外部操作改变的数据
· 不能改变外部状态
根据这上述条件来看,在 Javascript 的世界中去维持绝对纯净是不可能的,因为缺少了大多数函数式语言中使用的高效、不变的数据结构。我们知道在 Javascript 拥有能力去 freeze() 对象,但是只能对接对象的顶级属性,这就意味着一个嵌套对象下的属性是仍然能够被更改的。
var obj = Object.freeze({
foo: ’hello’,
bar: {
text: ’world’
}
})
obj.foo = ’goodbye’;console.log(obj.foo); // hello
obj.bar.text = ’goobye’;console.log(obj.bar.text); // goodbye
 ES6 中新增的 const 关键字,使用 const 可以定义一个不能够被重新赋值为不同的值,但是一个 const 对象的属性还是可变的。
const obj = ’hello’;
obj = ’goodbye’;    // Uncaught TypeError: Assignment to constant variable.
const obj = {
foo: ’hello’,
bar: ’world’
}
obj.foo = ’goodbye’;
console.log(obj);     // {foo: ’goodbye’, bar: ’world’}
 Javascrpt 中实现综合不变性还有很长的路要走。换句话来说,虽然不能够保证绝对的纯净,但是我们可以将纯净的部分抽离出来,将变化的影响降到最低,使得代码变得更加通用和容易测试。
总结:
· 函数式编程是支持一等函数的,函数具有其他数据类型相同的功能
· 函数式编程中使用闭包来进行数据的封装
· 使用高阶函数来建立代码的抽象,使代码更加灵活通用
· 尽量抽离纯函数来保持代码的可测性和通用性
来源:稀土掘金

《javascript函数式编程思想》

自序伴随着Web技术的普及,JavaScript已成为应用最广泛的编程语言之一。由于其在Web前端编程中的统治地位、语言本身的表现力、灵活性、开源的本质和ECMAScript标准近年来的快速发展,JavaScript向各个领域渗透的势头仍然... 查看详情

函数式编程javascript

给我的两个任务似乎很难理解,在完成我认为模块领导者想要的任务之前,还有其他人可以围绕这些任务吗?创建一个名为“hasMatch”的函数,该函数接受函数和列表,如果函数对列表中的至少一个项返回true,则返回true,否则... 查看详情

javascript函数式编程

第1章JavaScript函数式编程简介11.1JavaScript案例11.2开始函数式编程41.2.1为什么函数式编程很重要41.2.2以函数为抽象单元71.2.3封装和隐藏91.2.4以函数为行为单位101.2.5数据抽象141.2.6函数式JavaScript初试171.2.7加速191.3Underscore示例221.4总结2... 查看详情

《javascript函数式编程思想》

自序伴随着Web技术的普及,JavaScript已成为应用最广泛的编程语言之一。由于其在Web前端编程中的统治地位、语言本身的表现力、灵活性、开源的本质和ECMAScript标准近年来的快速发展,JavaScript向各个领域渗透的势头仍然... 查看详情

javascript系列:函数式编程(开篇)

...数以及函数柯里化等高级函数应用,同时,因为正在学习JavaScript·函数式编程,想整理一下函数式编程中,对于我们日常比较有用的部分。 为什么函数式编程很重要?   学习过C++,java这些面向对象编程语言,我... 查看详情

javascript进阶笔记

js是一门函数式语言,因为js的强大威力依赖于是否将其作为函数式语言进行使用。在js中,我们通常要大量使用函数式编程风格。函数式编程专注于:少而精、通常无副作用、将函数作为程序代码的基础构件块。在函数式编程中... 查看详情

javascript函数式编程基础(代码片段)

javascript函数式编程基础函数调用引用做返回值/*javascript函数式编程基础*/functionsayHello()return"helloworld";letresult=sayHello();//函数调用letfn=sayHello;//函数引用console.log(fn());//helloworld//函数做返回值functionee( 查看详情

玩转javascript面试:何为函数式编程?(代码片段)

函数式编程在JavaScript领域着实已经成为一个热门话题。就在几年前,很多JavaScript程序员甚至都不知道啥是函数式编程,但是就在近三年里我看到过的每一个大型应用的代码库中都包含了函数式编程思想的大规模使用。函数式编... 查看详情

Javascript 是函数式编程语言吗?

】Javascript是函数式编程语言吗?【英文标题】:IsJavascriptaFunctionalProgrammingLanguage?【发布时间】:2011-04-2703:08:08【问题描述】:仅仅因为函数是一等对象,有闭包和更高阶的函数,Javascript是否应该被称为函数式编程语言?我认为... 查看详情

javascript与函数式编程

原文:https://bethallchurch.github.io/JavaScript-and-Functional-Programming/译文:http://www.zcfy.cc/article/1013译者注:推荐一篇译文,《函数式编程术语解析》。本文是我在2016年7月29号听KyleSimpson精彩的课程《Functional-LightJavaScript》时所做的笔记... 查看详情

javascript函数式编程

编程范式编程范式是一个由思考问题以及实现问题愿景的工具组成的框架。很多现代语言都是聚范式(或者说多重范式):他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等。函数式编程范式函... 查看详情

《javascript函数式编程思想》——从面向对象到函数式编程

...假如本书的写作时间倒退回十年前,书名可能会变成JavaScript面向对象编程思想。自上世纪90年代兴起的面向对象编程思想随Java的繁荣达于顶点,在JavaScript从一门只被用来编写零星的简单的表单验证代码的玩具语言变成日... 查看详情

javascript函数式编程(代码片段)

JavaScript函数式编程(一) JavaScript函数式编程(二)在第二篇文章里,我们介绍了 Maybe、Either、IO 等几种常见的Functor,或许很多看完第二篇文章的人都会有疑惑:『这些东西有什么卵用?』事实上,如果只是为了学... 查看详情

简单的 Node/Express 应用程序,函数式编程方式(如何在 JavaScript 中处理副作用?)

】简单的Node/Express应用程序,函数式编程方式(如何在JavaScript中处理副作用?)【英文标题】:SimpleNode/Expressapp,thefunctionalprogrammingway(Howtohandleside-effectsinJavaScript?)【发布时间】:2017-12-2420:50:52【问题描述】:关于JavaScript中的函... 查看详情

javascript函数式编程-包含闭包链式优化及柯里化

本文着重介绍个人理解的函数式编程。函数式编程个人理解为:以函数为主要载体的编程方式。好处:语义更加清晰可复用性高可维护性好作用域局限、副作用少基本函数式编程://实现数组中每个单词首字母大写//一般写法const... 查看详情

javascript与函数式编程

原文:https://bethallchurch.github.io/JavaScript-and-Functional-Programming/译文:http://www.zcfy.cc/article/1013译者注:推荐一篇译文,《函数式编程术语解析》。本文是我在2016年7月29号听KyleSimpson精彩的课程《Functional-LightJavaScript》时所做的笔记... 查看详情

javascript-underscore与函数式编程

《Javascript函数式编程 PDF》#csdn下载地址http://download.csdn.net/detail/tssxm/9713727 Underscore #githubhttps://github.com/jashkenas/underscore#中文官方网站http://www.css88.com/doc/underscore/#CDN<s 查看详情

《javascript函数式编程思想》——类型系统

第2章 类型系统为什么在许多编程语言中整数和浮点数是两种类型?结构体、数组、列表、映射……这些类型有什么关系?用户自定义的各种类型与它们又有什么关系?函数也是类型吗?强类型和弱类型意味着什... 查看详情