JavaScript:了解一下函数式编程
一、简介
在JavaScript中,函数就是第一类公民,它可以像字符串、数字等变量一样,使用var修饰并作为数据使用。它可以作为数值、可以作为参数、还可以作为返回结果。可以说JavaScript就是函数式编程。ES6新语言特性中,箭头函数、扩展运算符会极大地帮助开发者利用函数式编程技术进行开发。
//定义一个箭头函数
var print = (message) => console.log(message)
1、函数作为数值使用
//将函数作为数值使用
const array = ["XYQ",print]
console.log(array) //["XYQ", function] (2)
2、函数作为参数使用
//将函数作为参数使用
//定义一个函数lg,它接收一个参数logger函数,lg的函数体内部会执行这个参数logger函数
//由于传入的print函数可以接收一个参数,所以logger函数在执行时,默认传入了一个字符串
const lg = logger => logger("I Am XYQ")
lg(print) //I Am XYQ
3、函数作为返回值使用
//将函数作为返回值使用
//定一个函数fcVale,它接收一个logger函数,同时它的返回值为新的函数,新的函数可以传入
//一个变量进行打印。最终,用函数fcVale创建一个函数fc,并给fc函数传入字符串进行调用
// var fcVale = function (logger) {
// return (message) => logger(message.toUpperCase())
// }
const fcVale = logger => message => logger(message.toUpperCase()) //高阶函数, 函数既可以接收函数参数,也可以作为其他函数返回值
const fc = fcVale(print)
fc("i am xyq") //I AM XYQ
二、风格
在JavaScript开发中,对于函数的编程分为两种风格,分别是命令式和声明式,其中函数式编程就是声明式的一部分。所谓命令式,就是更加重视函数为达到目标的执行过程而不是结果,也即重执行轻结果。而声明式,则恰恰相反,对执行结果的描述远胜于执行过程,声明式的函数很容易理解它的用途是什么,至于具体的执行细节则被隐藏了。在JavaScript中,声明式函数的编程使用及其广泛。
//定义一个字符串变量
var userString = "My Name is XYQ"
1、命令式
//命令式:遍历字符串,将字符串的空格全部用下划线替换 【开发者必须看完这个代码快才能知道函数的作用是替换功能】
var temp = ""
for (var i=0; i<userString.length; i++){
if (userString[i] === " "){
temp += "_"
} else {
temp += userString[i]
}
}
console.log(temp) //My_Name_is_XYQ
2、声明式
//声明式:使用正则表达式,将字符串的空格全部用下划线替换 【开发者看到replace就基本知道函数的用途就是替换功能】
const user_string = userString.replace(/ /g,"_")
console.log(user_string) //My_Name_is_XYQ
三、概念
函数式编程是JavaScript中的核心功能,它的核心概念一共有四种,分别是不可变性、纯函数、数据转换、高阶函数以及递归。
1、不可变性,说的是在函数式编程中,数据是不可以改变的,他们永远无法修改,实现不可变的工作机制就是对原生数据进行拷贝编辑,然后取代使用。
<script type="text/javascript">
//定义一个person对象
var person = {
name: "XYQ",
age: 25,
sex: "male"
} //方法一:通过Obejct.assign方法拷贝机制,创建一个空白对象,并将当前对象拷贝到空白对象上,接着重写对象值
var copyPerson = function (person, name) {
return Object.assign({}, person, {name:name}) // {} 为一个空白对象, person拷贝到{}上,重写name值
}
var newPerson = copyPerson(person,"YPX")
console.log(newPerson.name) //YPX
console.log(person.name) //XYQ,可以看到原对象没有发生改变 //方法二:通过扩展运算符特性对对象进行拷贝
const copyPerson2 = (person, name) => ({
...person,
name
})
var newPerson2 = copyPerson(person,"YPX")
console.log(newPerson2.name) //YPX
console.log(person.name) //XYQ,可以看到原对象没有发生改变 //---------------------------------------------------------------------------------// //定义一个数组对象
var persons = [
{name:"XYQ"},
{name:"YPX"}
] //方法一:使用数组的Array.concat方式将数组串联起来,生成一个新的对象并添加原生数组的副本上,不可以用Array.push这个可变函数
const addPerson = (name, persons) => persons.concat({name})
var newPersons = addPerson("HXF", persons)
console.log(newPersons) //[{name: "XYQ"}, {name: "YPX"}, {name: "HXF"}] (3)
console.log(persons) //[{name: "XYQ"}, {name: "YPX"}] (2),可以看到原数组没有发生改变 //方式二:使用扩展运算法特性对数组进行拷贝
const addPerson2 = (name, persons) => [...persons, {name}]
var newPersons2 = addPerson2("HXF", persons)
console.log(newPersons2) //[{name: "XYQ"}, {name: "YPX"}, {name: "HXF"}] (3)
console.log(persons) //[{name: "XYQ"}, {name: "YPX"}] (2),可以看到原数组没有发生改变
</script>
2、纯函数,是一个返回结果只依赖输入参数的函数,它至少需要接收一个参数并且总是返回一个值或者其他函数,它把参数当做不可变数据使用,不做任何修改。
纯函数特点:
1、函数应该至少接收一个参数;
2、函数应该返回一个值或者其他函数;
3、函数不应该修改或者影响任何传给它的参数
<script type="text/javascript"> //定义一个person对象
var person = {
name: "XYQ",
age: 25,
sex: "male"
} //创建一个纯函数,返回值是根据参数preson生成的一个新的person,它没有改变参数person,更改的返回的新的person。
const updatePerson = person => ({
...person,
name:"YPX",
sex:"feMale"
}) //打印
var newPerson = updatePerson(person)
console.log(newPerson) // {name: "YPX", age: 25, sex: "feMale"}
console.log(person) // {name: "XYQ", age: 25, sex: "male"}, 原对象person没有发生改变 </script>
3、数据转换,函数式编程中由于数据的不可变性,它会将一种数据转换成另一种数据,使用函数生成转换后的副本进行状态的转换。
//定义一个city数组
const cities = ["BeiJing","ChongQing","ChongDu"] //使用系统函数Array.join()将数组用分隔符连接成字符串
var cityString = cities.join(",")
console.log(cityString) // BeiJing,ChongQing,ChongDu
console.log(cities) // ["BeiJing", "ChongQing", "ChongDu"] (5) //使用系统函数Array.filter()进行谓词过滤,这个谓词始终是一个返回布尔值的函数
//会访问数组每一个元素,匹配C开头的城市,如果返回true,则将其添加到新的数组中
const newCities = cities.filter(city => city[0] === "C")
console.log(newCities) // ["ChongQing", "ChongDu"] (2)
console.log(cities) // ["BeiJing", "ChongQing", "ChongDu"] (5) //仍然使用系统函数Array.filter(),代替Array.pop()和Array.splice()函数删除元素。因为后面的两个方法是可变的。
const deleteCity = (deletedCity, cities) => cities.filter(city => city !== deletedCity)
const newCities2 = deleteCity("BeiJing", cities)
console.log(newCities2) // [ "ChongQing", "ChongDu"] (4)
console.log(cities) // ["BeiJing","ChongQing", "ChongDu"] (5) //使用系统函数Array.map(),参数也是一个函数,在函数在访问数组中每一个元素时会执行
const newCities3 = cities.map(city => `${city} China`)
console.log(newCities3) // ["BeiJing China","ChongQing China", "ChongDu China"] (5)
console.log(cities) //["BeiJing", "ChongQing", "ChongDu"] (5) // 仍然使用系统函数Array.map(),她还可以构造任意对象、数值、数组、函数等
// 1、将数组转对象
let objectCities = cities.map(city => ({cityName : city}))
console.log(objectCities) // [{cityName: "BeiJing"}, {cityName: "ChongQing"}, {cityName: "ChongDu"}] (3)
console.log(cities) // ["BeiJing", "ChongQing", "ChongDu"] (3) // 2、修改对象元素
const updateCityName = (originCityName, cityName, cities) =>
(cities.map(city => (city.cityName === originCityName) ? ({...city, cityName}) : city))
const newObjectCities = updateCityName("BeiJing","TianJin",objectCities)
console.log(objectCities) //[{cityName: "BeiJing"}, {cityName: "ChongQing"}, {cityName: "ChongDu"}] (3)
console.log(newObjectCities) //[{cityName: "TianJin"}, {cityName: "ChongQing"}, {cityName: "ChongDu"}] (3) // 3、将对象转数组(配合Object.key函数使用)
//定义一个city对象
const cityObject = {"BeiJing":"China","NewYork":"USA"}
const cityObjects = Object.keys(cityObject).map(key =>
({cityName: key, country: cityObject[key]})
)
console.log(cityObject) //{BeiJing: "China", NewYork: "USA"}
console.log(cityObjects)//[{cityName: "BeiJing", country: "China"}, {cityName: "NewYork", country: "USA"}](2) // 系统函数Array.reduce()和rArray.reduceRight()可以用来将数组转成任意值,比如数字、字符串、布尔值、对象甚至函数
// Array.reduce()函数, 从数组头部开始处理元素 ; Array.reduceRight()函数,从数组尾部 开始处理元素
// 求最值
const nums = [10,5,30,24,78,60,100]
const max = nums.reduce((max, num) => (num > max) ? num : max)
const min = nums.reduce((min, num) => (num < min) ? num : min)
console.log(max) //
console.log(min) // // 去重,items为新的空数组
const names = ["XYQ","YPX","XXF","XYQ","XYQ"]
const deletedNames = names.reduce((items, name)=> items.indexOf(name) !== -1 ? items : [...items,name], [])
console.log(names) //["XYQ", "YPX", "XXF", "XYQ", "XYQ"] (5)
console.log(deletedNames) //["XYQ", "YPX", "XXF"] (3)
4、高阶函数,是可以操作其他函数的函数,它们可以将函数当做参数传递,也可以返回一个函数,或者二者兼有之。柯里化就是典型的应用。
//定义高阶函数,把函数当做参数
const printer = () => console.log("---printer---")
const logger = () => console.log("---logger---")
const show = (selectPrint, printer, logger) => (selectPrint) ? printer() : logger() //调用高阶函数
show(true,printer,logger) //---printer---
show(false,printer,logger) //---logger--- //定义高阶函数,把函数当做返回值
const log = (message) =>( () => console.log(message) )
const logFunction = log("I Am XYQ") //调用高阶函数
logFunction() //I Am XYQ
5、递归,它是用户创建的函数调用自身的一种技术方案,在开发中遇到循环时,通过递归可以极其精简的缩短代码量,是一种优势替代方案。
//定义一个倒序遍历的函数
const countDown = (number, log) => {
log(number)
return (number > 0) ? countDown(number-1, log) : number
} //调用递归函数
const log = (number) => console.log(number)
countDown(5,log)// 5 4 3 2 1 0
四、链式
函数式编程是将具体的业务逻辑拆分成一个个小型的简单的纯函数,方便进行功能聚焦,最终,开发者通过串联或者并联的方式将这些小型函数合成在一起进行调用即可。这个合成的过程其实就是链式调用。如之前介绍的Promise对象的应用。
//使用系统函数replace实现链式调用
const formmater = "hh:mm:ss tt"
const currentTime = formmater.replace("hh", "22").replace("mm","07").replace("ss","00").replace("tt","PM")
console.log(currentTime) //22:07:00 PM
JavaScript:了解一下函数式编程的更多相关文章
- javascript - Underscore 与 函数式编程
<Javascript函数式编程 PDF> # csdn下载地址http://download.csdn.net/detail/tssxm/9713727 Underscore # git ...
- 给 JavaScript 开发者讲讲函数式编程
本文译自:Functional Programming for JavaScript People 和大多数人一样,我在几个月前听到了很多关于函数式编程的东西,不过并没有更深入的了解.于我而言,可能只 ...
- 通过代数,数字,欧几里得平面和分形讨论JavaScript中的函数式编程
本文是对函数式编程范式的系列文章从而拉开了与以下延续一个. 介绍 在JavaScript中,函数只是对象.因此,可以构造函数,作为参数传递,从函数返回或分配给变量.因此,JavaScript具有一流的 ...
- JavaScript系列:函数式编程(开篇)
前言: 上一篇介绍了 函数回调,高阶函数以及函数柯里化等高级函数应用,同时,因为正在学习JavaScript·函数式编程,想整理一下函数式编程中,对于我们日常比较有用的部分. 为什么函数式编程很重要? ...
- [置顶] Ruby,Scala和JavaScript中的函数式编程(一)
函数式编程(英语:Functional programming)或者函数程序设计,又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象.函数编程语言最重要的 ...
- 翻译连载 | 第 11 章:融会贯通 -《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- 快速入门函数式编程——以Javascript为例
函数式编程是在不改变状态和数据的情况下使用表达式和函数来编写程序的一种编程范式.通过遵守这种范式,我们能够编写更清晰易懂.更能抵御bug的代码.这是通过避免使用流控制语句(for.while.brea ...
- JavaScript中函数式编程中文翻译
JavaScript 中的函数式编程 原著由 Dan Mantyla 编写 近几年来,随着 Haskell.Scala.Clojure 等学院派原生支持函数式编程的偏门语言越来越受到关注,同时主流的 ...
- JavaScript与函数式编程
JavaScript与函数式编程 绝大多数编程语言都会有函数的概念(或者说所有的?我不太确定),他们都可以做出类似的操作: function(x) { return x * x } 但是Javascr ...
- 拥抱函数式编程 I - 基本概念
函数编程与命令性编程 为支持使用纯函数方法解决问题,特此创建了函数编程范例. 函数编程是一种声明性编程形式.相比之下,大多数主流语言,包括面向对象的编程 (OOP) 语言(如 C#.Visual Ba ...
随机推荐
- iOS-----------安装fir-cli错误
1.在终端执行 gem install fir-cli 一直提示错误: You don't have write permissions for the /Library/Ruby/Gems/ ...
- SVN安装及其汉化
1.百度搜索SVN,点击官网进去 2.点击download进入下载页面,选择合适的安装包 3.当前页面往下拉,看到汉化包下载页面,要注意版本 4.2个下载完,先安装软件在安装汉化包,要注意软件和汉化包 ...
- Error: Unable to establish IPMI v2 / RMCP+ session
是这样的,需要ipmi控制器,然后一直报错这个: [root@localhost ~]# sudo ipmitool -I lanplus -H 192.168.87.12 -U root -P pa ...
- LeetCode刷题--两数相加(中等)
题目描述 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表 ...
- Tkinter使frame填充整个区域
在未设置默认情况下效果为 设置之后出现效果 使用方法: 增加 fill=X/Y/BOTH 以我为例: leftFrame.pack(side='left',fill=Y)
- C++之重载覆盖和隐藏
继承体系下同名成员函数的三种关系 重载 在同一作用域内 函数名相同,参数列表不同(分三种情况:参数个数不同,参数类型不同,参数个数和类型都不同) 返回值类型可以相同也可以不同 重写(覆盖) 在不同作用 ...
- Vue小练习 02
用table标签渲染下面的数据, 最后一列为总分, 第一列为排名 scores = [ {name: 'Bob', math: 97, chinese: 89, english: 67}, {name ...
- Swoole编译安装步骤
Swoole扩展是按照php标准扩展构建的.使用phpize来生成php编译配置,./configure来做编译配置检测,make进行编译,make install进行安装. 请下载releases版 ...
- Web安全测试学习笔记-DVWA-图片上传
很多网站都有上传资源(图片或者文件)的功能,资源上传后一般会存储在服务器的一个文件夹里面,如果攻击者绕过了上传时候的文件类型验证,传了木马或者其他可执行的代码上去,那服务器就危险了. 我用DVWA的文 ...
- c#时间戳相互转换
/// <summary> /// 获取时间戳 /// </summary> /// <returns></returns> public static ...