函数式编程及其在react中的应用

更新时间:2023-05-29 15:57:21 阅读: 评论:0

函数式编程及其在react中的应⽤
开头:初衷是想写⼀篇介绍redux的分享,结果阅读源码时发现看懂源码还必须先对函数式编程有⼀点的了解,结果写着写着就变成了⼀篇介绍函数式编程的⽂章,也罢...
函数式编程与js
1. ES6+ 语法对于函数式编程更为友好
2. RxJS 、redux等青睐函数式的库的开始流⾏,
3. 带有‘函数式’标签的框架开始流⾏,诸如react.
从react认识函数式编程
这篇⽂章⾥我将略去⼀⼤堆形式化的概念介绍,重点展⽰在 JavaScript 中⼀些常见的写法,从例⼦讲述到底什么是函数式的代码、函数式代码与⼀般写法有什么区别、函数式的代码能给我们带来什么好处以及在react全家桶中常见的⼀些函数式模型都有哪些。
枯燥的概念
"函数式编程"是⼀种"编程范式",也就是如何编写程序的⽅法论,就像我们熟知的⾯向对象编程⼀样。
我眼中的函数式编程
以函数作为主要载体的编程⽅式,⽤函数去拆解、抽象⼀般的表达式,最⼩‘可视’单位是函数的⼀种编码规范,重点体现‘函数式’,
理解命令式or声明式开始
命令式代码的意思就是,我们通过编写⼀条⼜⼀条指令去让计算机执⾏⼀些动作,这其中⼀般都会涉及到很多繁杂的细节,⾯向的是命令的过程。
与命令式不同,声明式意味着我们要写表达式(命令的意图),⽽不是⼀步⼀步的具体的指⽰
注:表达式"是⼀个单纯的运算过程,总是有返回值;"语句"是执⾏某种操作,没有返回值。函数式编程要求,只使⽤表达式,不使⽤语句。也就是说,每⼀步都是单纯的运算,⽽且都有返回值。
// 命令式
const arr = ['apple', 'pen', 'apple-pen'];
for(const i in arr){
const c = arr[i][0];
arr[i] = c.toUpperCa() + arr[i].slice(1);
三国杀孔融
}
// 声明式
// 函数式写法
function upperFirst(word) {
return word[0].toUpperCa() + word.slice(1);
}
function wordToUpperCa(arr) {
return arr.map(upperFirst);
}
console.log(wordToUpperCa(['apple', 'pen', 'apple-pen']));
复制代码
建筑设计方案声明式的写法是⼀个表达式(这⾥是⼀个函数表达式),如何进⾏计数器迭代,返回的数组如何收集,这些细节都隐藏了起来。它指明的是做什么,⽽不是怎么做。更加清晰和简洁之外,我们可以通过函数命名⼀眼就能知道它⼤概做了些什么,⽽不需要关注它内部的实现
这⾥(声明式)的⼀些明显的好处
1. 语义更加清晰(依赖简洁有效的命名)
2. 可复⽤性更⾼(函数为可调⽤的最⼩单位)
3. 可维护性更好(只需关注表达式的内部的实现,更易定位bug)
4. 作⽤域局限,副作⽤少(es6之后局部作⽤域的⽀持)
为什么称react组件为‘声明式’UI?
function TodoListComponent(props) {
return (
<ul>
{dos.map((message) => <Item key={message} message={message} />)}
</ul>
);
灰毛衣番号}
class Demo extends Component {
render() {
return (
<View></View>
)
}}穷理
复制代码
很简单的判定⽅式,每次都有返回值(注意return)
函数是‘⼀等公民’
这句话解释起来就是:函数和其他js其他数据类型都⼀样...
再详细⼀点:你可以像对待任何其 他数据类型⼀样对待它们——把它们存在数组⾥,当作参数传递,赋值给变量...等 等。
所以我们将以函数为参数并且返回了另⼀个函数的函数称之为⾼阶函数:
接受⼀个或多个函数作为输⼊
输出⼀个函数
⼤概的模样:
function gaojiefn(fn) {
//...
return enhandceFn // 返回⼀个增强过的函数
}复制代码
作⽤:函数功能的增强
在react中对应⾼阶函数就是⾼阶组件(hoc),顾名思义就是⼀个接受组件为参数并且返回⼀个组件的函数,它的强⼤之处在于能够给任意数量的组件提供数据源,并且可以被⽤来实现逻辑复⽤。 最最常见的例⼦:react-redux中的connect函数
state => state.ur,
{ action }
)(App)
复制代码
其作⽤就是在组件APP外层包裹了⼀个⽤以获取redux储存的state和action的容器,我们通常称他们为容器组件。
另外⼀个典型的例⼦就是react-router-v4提供的withRouter(),⽤来包裹任何需要获取路由信息的组件。 当然我们也可以发挥想象,⾃由发挥,⾃定义我们的hoc,来公⽤我们想共享的逻辑:
const newCom = (WrapedCom) => {
//...公⽤的逻辑
return <WrapedCom  {...props} />
比值怎么求}复制代码
总结:⾼阶组件在react中⾮常重要,配合es7⽀持的装饰器写法,强⼤⽽优美
没有副作⽤的纯函数
拿数组操作的 slice 和 splice做⽐较
var xs = [1,2,3,4,5];
// 纯的
长沙好吃的xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
装模作样的拼音//=> [1,2,3]
// 不纯的
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
/
/=> [4,5]
xs.splice(0,3)
//=>[]
复制代码
这两个函数的作⽤并⽆⼆致——但是注意,它们各⾃ 的⽅式却⼤不同。我们说 slice 符合纯函数的定 义是因为对相同的输⼊它保证能返回相同的输出(每次返回新数组)。⽽ splice 则会在原先的数组操作,并修改原先的数组。
通过上⾯的例⼦ 我们⼤概知道,纯函数⼤概就是这样⼦
对于相同的输⼊,永远会得到相同的输出,⽽且没有任何可观察的副作⽤,也不依赖外部环境的状态。
在函数中要怎么做?
不依赖不改变外界环境
var minimum = 21;
var checkAge = function(age) {
return age >= minimum;
};
// 纯的
var checkAge = function(age) {
var minimum = 21;
return age >= minimum;
};
公务员体检项目
// 不纯的
var count = 1
function add() {
return count + 1
}
// 纯的
function add() {
var count = 1
return count + 1
}复制代码
在我们从依赖性和改变外界环境的两个⾓度举了两组例⼦
不纯的版本中,函数 的结果将取决于 外部变量 甚⾄ 修改外部变量。从⽽增加了认知负荷,有时候甚⾄引发bug,这就是为什么我们刚开始写js时,总有⼈警告我们不要设置全局变量。
在纯函数中他们总只着眼于⾃⼰的⼀亩三分地,不会给别⼈(程序员)添⿇烦。
当然这并不是要求我们不要‘副作⽤’,⼀个有完整的程序总会产⽣‘副作⽤’,关键是我们怎么去优雅的处理它们(在函数式的理念中可以定义⼀种有⽤的‘functor’去包裹‘副作⽤’)
优点:
1.可缓存性
import _ from 'lodash';
var sin = _.memorize(x => Math.sin(x));
//第⼀次计算的时候会稍慢⼀点
var a = sin(1);
//第⼆次有了缓存,速度极快
var b = sin(1);
复制代码
利⽤了lodash的memorize函数,第⼀次会在内存在记忆住了sin(1)的值,第⼆次的时候直接从内存中提取以来加快了运⾏速度,提升了性能(切记只有纯函数才能这样⼦储存起来,因为他们对相同的输⼊有相同的输出)
不知道你有没有⽤过 relect 这个库
redux state的任意改变都会导致所有容器组件的mapStateToProps的重新调⽤,进⽽导致使⽤到lectors重新计算,但state的⼀次改变只会影响到部分letor的计算值,只要这个lector使⽤到的state的部分未发⽣改变,lector的计算值就不会发⽣改变,理论上这部分分计算时间是可以被节省的。 relect正是⽤来解决这个问题的,它可以创建⼀个具有记忆功能的lector,但他们的计算参数并没有发⽣改变时,不会再次计算,⽽是直接使⽤上次缓存的结果。从⽽优化了性能。
2.可测试性
相同输⼊=>相同输出 这简直是单元测试梦寐以求的
3.引⽤透明
纯函数是完全⾃给⾃⾜的,它需要的所有东西明确表⽰。仔细思考思考这⼀ 点...这种⾃给⾃⾜的好处是什么呢?纯函数的依赖很明确,因此更易于观察 和理解,更加容易定位bug的位置
再谈柯⾥化
由⼀道经典⾯试题⼊⼿:
add(1)(2)(3) = 6
add(1, 2, 3)(4) = 10
add(1)(2)(3)(4)(5) = 15
实现⼀个通⽤的add函数?复制代码
function add() {
var _args = [];
return function(){
if(arguments.length === 0) {
return _duce(function(a,b) {
return a + b;
});
}
[].push.apply(_args, [].slice.call(arguments));
return arguments.callee;
}
}
复制代码
上⾯的实现,利⽤闭包的特性,主要⽬的是想通过数组操作的⽅法将所有的参数收集在⼀个数组⾥,并最终传⼊reduce将数组⾥的所有项加起来。因此我们在调⽤add⽅法的时候,参数就显得⾮常灵活(随意组合,⽆关顺序)。
柯⾥化==颗粒化
柯⾥化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后,部分应⽤参数,并返回⼀个更具体的函数接受剩下的参数,中间可嵌套多层这样的接受部分参数函数,逐步缩⼩函数的适⽤范围,逐步求解,直⾄返回最后结果。
通俗的讲:
函数柯⾥化允许和⿎励你分隔复杂功能变成更⼩更容易分析的部分。这些⼩的逻辑单元显然是更容易理解和测试的,然后你的应⽤就会变成⼲净⽽整洁的组合,由⼀些⼩单元组成的组合。
优点:
1.提⾼通⽤性
当我们把⼀个复杂逻辑的函数拆分成,⼀个个更⼩的逻辑单元时,函数组合(代码服⽤)将变得更加的灵活且维护测试更简单。
2.延迟执⾏
3.固定易变因素(bind)
⼀道⾯试题,如何⽤call,apply来实现bind的功能

本文发布于:2023-05-29 15:57:21,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/807555.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:函数   参数   组件   代码   编程   返回
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图