获取中...

-

Just a minute...

react不是魔法

今天我想来聊一聊函数式编程,和它在react中的体现。

何为函数式编程

我们可以把函数式编程理解成一种范式,一个规范。纯函数是函数式编程的关键概念,函数式编程希望我们尽可能多地使用纯函数。正是依赖于纯函数的特点,函数式编程使得我们对于程序的结果具有更高的掌控性。

至于函数式编程中的什么,函数是第一等公民啊,函数柯里化,高阶函数,闭包,什么之类的,别的文章里面讲烂的东西我也不赘述了,我只想讲讲纯函数。

纯函数

我今天不说编程意义上普遍的“函数”(也被叫做方法),我想谈谈数学上的函数和编程上的纯函数。

数学意义上的函数,近代定义就是定义域到值域的对应关系。通俗地说,我们把函数理解成一个机器(对应关系),给它一个输入,它给你返回一个输出。如果你给它相同的输入,它能确保给你相同的输出,那么它就是一个函数。

纯函数是指,你编写的函数,也能做到和数学意义的函数相同的事情,并且不能有副作用。即以下两点

  • 首先,在决定输出的时候,所依赖的东西必须是输入的子集。不能有会变的因素影响到函数的输出。比如我要输出一个数字,我不能根据当时的时间和天气来得到结果,也不能因为某个定义在函数外部的变量得到结果。这些都会导致输入不变时,输出发生变化。
  • 其次,函数不能有副作用。什么算副作用呢?所有影响了函数外部的东西的操作,都是副作用(side effect,不是负作用)。比如你修改了外部的一个变量,发送了一个请求,打印了一个数据,这些都是副作用。为啥叫做副作用呢,因为这些操作都是与函数不相关的,它们使得函数的影响不局限于函数本身之内了。这违背了函数式编程的初衷。副作用使得函数的影响范围不可控。
    值得注意的是,改变了函数的实参,也是副作用。因为实参是定义在函数外部的变量。一个很好的例子是sort(Array.prototype.sort),它就不是一个纯函数。当你调用a.sort()之后,a变成了排序后的数组。a当然也是sort的参数,因为对于sort来说,a就是this。而sort改变了this。所以说sort不是一个纯函数。
    这其实是一个很不好的事,通常在这种情况下,我们应该新建一个对象,将新建的对象返回,而不是在原来的对象上面做更改。当然代价是更多的内存,不过这换来了更高的可调试性。我们试想一下,你写了一个会改变实参的方法,别人把自己定义的变量作为参数传给你,结果变量却被改变了,别人会被你气死,因为他万万想不到,变量的值不符合预期,竟然是因为变量作为参数传给了你。

换句话说,纯函数的要求是相同输入对应相同的输出,并且不能对函数之外的部分造成影响。纯函数有以下优点:

  1. 可缓存性:因为相同的输入有相同的输出,所以可以把某个输入对应的输出缓存起来,下次拿到相同的输入时,直接返回缓存的结果,不必重新计算。
  2. 可测试性(重要)或者说,可预期的:纯函数测试起来很简单,你能完全掌握这个函数做的事,只需要通过观察它的输出是否正确。而如果有副作用的非纯函数话,函数对外界做出了影响,这些影响无法通过函数的输出检测到,容易被忽略,但是又可能会导致bug。这就是为什么纯函数比非纯函数好。
  3. 可并行性:因为纯函数不会对函数外界造成影响,因此多个纯函数就互不影响,可以并行运行,各玩各的。不过js是单线程语言,这点并不能体会到。
  4. 引用透明:即调用这个函数的语句,可以被函数计算的结果替代。

由于这些优点,纯函数可以大大降低代码的耦合关系,使得结果完全可以预计,你写的变量也不会被别人的代码改变值。这样会使得开发与调试的难度大幅降低。因此,我们在开发的过程中,都应该注意尽量使用纯函数。

React中的函数式编程

react不是魔法

首先解释下“react不是魔法”这句话。是的,react不是魔法,它只是JavaScript代码。

你大可以把他当做一个黑盒子,你学着别人的样子,写着按照jsx语法写着代码,然后你的代码就出现在了屏幕上,就像我之前写vue时一样。我当初用着vue-cli的时候,根本不知道自己写的代码是如何变成dom的,只知道写template,script和style三部分。我不是在黑vue,只是说react的写法看起来更加原始,更能够让人们明白,自己写的每一行代码是如何变成dom的。当然vue也有很多不用脚手架的方法,写起来也不那么“黑盒”,只是当初我完全看不懂,也就无从深入了解罢了。

而实际上,react就是需要你去写一个jsx,然后将这个jsx转成React.createElement。通过这个API去构造虚拟节点。而这个jsx,其实就是类组件中render()的返回值,或者函数组件的返回值。

react的组件其实就是函数调用

我们这里取最简洁的函数组件为例,暂时不考虑使用hooks的情况,函数组件就是一个纯函数,接受一个props,返回一段jsx。react的渲染,其实就是首先用createRoot方法渲染根组件,然后按照组件的层级,一层一层地向下调用函数罢了。我始终忘不掉我之前用Remax写小程序的时候,看到最开始的app.js时的震撼。

1
2
3
4
5
import './app.css'

const App = props=>props.children

export default App

一开始我不明白这是啥意思啊,后来我知道,原来这就是根组件啊。我没在它里面添加逻辑,它就只需要把它的子组件返回出去,然后让它的子组件去渲染就好了。因为箭头函数的语法,整个组件的代码简洁的令人震惊。看明白了之后我感觉我更加理解react组件是什么东西了。如果只是用函数组件(不带hooks)的话,react就是函数式编程,它的渲染就是一个一个纯函数的调用。

当然了,一个react应用,没有副作用的话很难实现功能,因为发送请求等等都是副作用。所以副作用是几乎不可避免的,函数组件在hooks出现之前功能是不完备的。而函数式编程,也只是一个范式,一个规范,我们只能尽可能去遵守。后面有时间的话,再来说一说带hooks的函数组件,和函数式编程的关系吧。

相关文章
评论
分享
  • react的一些坑

    记录一些刚写react踩过的坑吧,想到多少就写多少 从一个工作中的需求开始有个需求是,点击按钮出来一个弹窗。本来弹窗有个关闭按钮,但是UED在视觉稿上面加了行字,“点击关闭按钮或弹窗外的其他地方,关闭弹窗”。我想了想,合理,避免有...

    react的一些坑
  • 使用react的一些心得与感想

    刚忙完公司的新人训练营,9个人的团队2周完成一个小项目。我负责用remax开发一个微信小程序。remax就是蚂蚁的一个开源框架,让开发者能用react的语法去开发微信小程序。开发体验整体来讲非常不错,感觉也加深了我对react的理解...

    使用react的一些心得与感想
  • 一些部署相关的名词解释

    毕业刚入职,看到公司的各种发布,部署平台,眼花缭乱,看文档的过程中也看到了很多不懂的名词,了解了之后做一下记录 名词解释 serverless: 字面意思是“无服务器”,即开发者(开发公司)不用自己的机房服务器,采用云服务器厂商提...

    一些部署相关的名词解释
  • 动手实现react-ssr 2

    仓库链接:本篇代码 运行方法参考readme,请使用pnpm,因为本次使用的rspack尚未推出1.0版本,预计后续可能会有很多breakchange,所以lock文件至关重要,而我只上传了pnpm的lock,非常抱歉。 环境:no...

    动手实现react-ssr 2
  • 动手实现react ssr

    关于ssr相关的介绍,可以移步之前的一篇博文,本文不再赘述 日常工作中,我们可能都接触过ssr,不过我们可能通常是借助于框架(例如Next.js)的支持来完成ssr。今天我想尝试,摆脱高度封装的前端框架,只依赖react相关的库,来实...

    动手实现react ssr
  • 从invalid hook call报错说到npm install相关

    遇到了invalid hook call报错上周,业务下游的同事反馈了一个生产问题,说我们传的值有问题,领导让我帮忙排查一下。我最近一直没做那个项目相关的需求,打开那个项目切到release分支,拉代码,npm i然后启动,结果就白屏...

    从invalid hook call报错说到npm install相关
  • React中副作用的执行时机

    为什么一定要useEffect?之前看到过,说不要把副作用直接写在函数式组件的函数体内,需要用useEffect把副作用包裹起来。我当时的理解就是,函数组件重新渲染的话,整个函数都会被执行一遍。如果我就想让一个副作用,每次组件渲染都重...

    React中副作用的执行时机
  • 基于tapable来模拟webpack插件机制

    前两天看到ByteFE公众号发了篇文章,从源码去讲webpack。我之前也从很多方面去学习webpack,只是源码一直没太看得进去,这一次我想仔细地读一读。 简要介绍tapable说实话,之前看webpack源码一直没看的太懂,...

    基于tapable来模拟webpack插件机制
  • 深入js——作用域,作用域链,执行栈(一)

    最近在补js基础。当初自学js的时候,总是感觉学的不扎实。只能说不去上手,再怎么死读书,也理解不了。积累了经验,然后自己去思考,这个时候再去看理论,就容易理解了。 感觉工作一年以来,确实学到了很多,但是过于专注于应用方面的知识,反倒...

    深入js——作用域,作用域链,执行栈(一)
  • webpack运行时代码简要分析

    开个坑,主要也是怕自己后面忘了 简单写写,有空了补上 __webpack_modules是一个数组,每个元素都是一个函数,函数接受三个参数:第一个参数module好像没用到,第二个是一个对象(会把本模块要导出的属性,赋值给这个对象),...

    webpack运行时代码简要分析
Please check the parameter of comment in config.yml of hexo-theme-Annie!