本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!
专栏简介
作为一名 5 年经验的 JavaScript 技能拥有者,笔者时常在想,它的核心是什么?后来我确信答案是:闭包和异步。而函数式编程能完美串联了这两大核心,从高阶函数到函数组合;从无副作用到延迟处理;从函数响应式到事件流,从命令式风格到代码重用。所以,本专栏将从函数式编程角度来再看 JavaScript 精要,欢迎关注!传送门
回顾
前 6 篇传送门:
- ✨从历史讲起,JavaScript 基因里写着函数式编程
- ✨从柯里化讲起,一网打尽 JavaScript 重要的高阶函数
- ✨从纯函数讲起,一窥最深刻的函子 Monad
- ✨从延迟处理讲起,JavaScript 也能惰性编程?
- ✨从异步讲起,『函数』和『时间』该作何关系?
- ✨从响应式讲起,Observable:穿个马甲你就不认识啦?(附实战)
专栏至此,本篇算是阶段性作结了。
数据一览
专栏的点赞率相对于其它文章还算是比较高的。
只不过基础的阅读量偏低,几篇加起来还抵不了一篇口水文,原因可能有 3 点:
- 平台对新文章的推送策略从 9 月份之后发生变化,转变为更侧重于推送旧的好文章;
- 专栏内容相对较干,更多人来社区看文章或图一乐、或为解决问题、或为面试、或为收集好资源;
- 更文频率下降,导致账号整体流量偏小(因为同时段在关注另一件事);
在没什么宣传的前提下,专栏关注人数接近 100 人,还不错,感谢大家支持~
其实数据只是一方面,没必要唯数据论。
好的东西应该是经得起时间的检验,我自己都会经常回过头来看一看这些文章内容,说明用心写过,至少自己是认同的。即使不完美,也是现阶段的成果。完成总好过完不成,完成甚至大于拖延的完美。
事情是一点点去做、一点点去推动的,只要还没盖棺定论,就有持续改进、优化的机会和空间。如果逃避,就只能跟这事儿说拜拜了。。。关键也逃不掉,过一段时间又会遇到它,所以别畏惧,一句老话:不怕慢,就怕站。
不忘初心
不忘初心,那完成后的专栏内容和最初的专栏主题设计是否是贴合的呢?
最开始的设计是:
- 关注 JavaScript 两个核心 —— “闭包” 和 “异步”;
- 函数式编程真的串联了这两个核心吗?
- 从高阶函数到函数组合;
- 从无副作用到延迟处理;
- 从函数响应式到事件流;
- 谈代码重用;
一言以蔽之:从函数式编程角度来看 JS 闭包和异步。
实际上说的:
- 闭包的起源,闭包刻在 javaScript 基因里;
- 柯里化思想,一网打尽高阶函数;
- 纯函数、无副作用、函数组合、函数怎样“尽可能保持纯”;
- 延迟处理、JS 惰性编程,联系闭包和异步;
- 函数响应式编程 FRP, RxJS Observable 事件流及实战;
- 本篇后文将浅谈代码重用;
OK,方向好像确实是这么一个方向,没走偏。
可惜就是没有生产出一个好的轮子,可以直接供业务开发中使用。这感觉就像:我知道这东西很牛b,但是就还不能发挥出它十足的威力。
fine,理论指导实践,实践是检验真理的标准。所以这里是“阶段性”作结,
代码复用
Vue2 mixin
本瓜以前把 mixin 当个宝,在 Vue2 的大型项目中用起来,这复用、那复用,一段时间就发现不对劲了,这东西的覆盖规则怎么这么复杂,代码溯源怎么这么难呢?
这合并策略,是个人看了都会头疼吧?
- 如果是data函数的返回值对象 返回值对象默认情况下会进行合并; 如果data返回值对象的属性发生了冲突,那么会保留组件自身的数据;
- 如果是生命周期钩子函数 生命周期的钩子函数会被合并到数组中,都会被调用; mixin中的生命周期钩子函数会比组件中的生命周期钩子函数先执行(全局mixin先于局部mixin,局部mixin先于组件);
- 值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。 比如都有methods选项,并且都定义了方法,那么它们都会生效; 但是如果对象的key相同,那么会取组件对象的键值对;
看到这个合并策略真的会“谢”,去定位问题的时候,到处 debugger,看看到底是进的哪一个钩子函数中。
mixin 缺点:
- 变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护。
组件里可以引入多个mixin,并直接隐式调用mixin里的变量/方法, 这会让我们有时候混乱 这些变量/方法 分别是哪个mixin里的?
- 多个mixins的生命周期会融合到一起运行,但是同名属性、同名方法无法融合,可能会导致冲突、很容易制造混乱。
- mixins和组件可能出现多对多的关系,复杂度较高(即一个组件可以引用多个mixins,一个mixins也可以被多个组件引用)。
狗都不爱。。。
这让人不禁联想到 JS 中同样让人头疼的东西,this 的绑定策略:
代码语言:javascript复制情况 1. 默认绑定
情况 2. 隐式绑定
情况 3. 显示绑定
情况 4. new 绑定
具体就不展开了,也同样让人会“谢”。
this 的绑定其实也是为了代码重用,同样搞得人头疼。完全不符合 JS 轻量、简单的气质。
不过,代码写都屎山已经铸成,就不要轻易挪动了。。。
Vue3 Setup
后来大佬又带来了 Vue3 Composition API ,“好呀好呀"
用类似于react hook 式的函数式组件:
隐式输入、输出,变成了显示输入、输出,这不就是函数式编程思想中无副作用的纯函数一直要求的吗?
还问函数式编程的“无副作用”有什么实际的应用吗?
这个函数式组件,也就是相当于是一个闭包环境,内部变量不会影响外部变量,如果有命名冲突的情况,解构重新赋值即可。
这样看起来,就舒服多了~~
与其说,Vue3 模仿 React hooks,不妨说它们都只是按照函数式编程的思路在演进罢了。
React class
React 也是啊。React V16.8 hooks 出来之前的 class 组件,this 的绑定之麻烦,定位问题查询起来之麻烦,也是 this 的指向规则、以及隐式的输入、输出导致的。
比如:某个组件从 3 个以上的高阶组件去复用逻辑。
代码语言:javascript复制this.props.xxx();
this.props.aaa();
this.props.bbb();
如果xxx出现了问题,如果对项目不熟悉的人的话想要找这个方法,就要分别去这三个高阶组件里面去找,或者去父组件里面去找。
React hooks
有了 hooks 的设计,
代码语言:javascript复制const { xxx } = useXXX();
const { aaa } = useAAA();
const { bbb } = useBBB();
哪个有问题,就去对应的位置找哪个,显示输出,就是能轻松定位来源。
写法上,也更加简便、直观了:
class Component:
代码语言:javascript复制class ExampleOfClass extends Component {
constructor(props) {
super(props)
this.state = {
count: 1
}
}
handleClick = () => {
let { count } = this.state
this.setState({
count: count 1
})
}
render() {
const { count } = this.state
return (
<div>
<p>you click { count }</p>
<button onClick={this.handleClick}>点击</button>
</div>
)
}
}
hooks:
https://code.juejin.cn/pen/7162070517803909120
小结
从 Vue2 mixin 到 Vue3 Composition API;从 react class 组件到 react hooks;
不用说,你都能感受到:
- 我们确实不喜欢隐式的输入、输出,对于代码的可读性太不又好了;
- 我们在复用的时候讨厌 this 指来指去;
- 千万不要在查找属性的时候,又要查同级的组件、父组件、父父组件,从哪来、到哪去,一定给说明白了。
复用思考
react 相对于 vue2 本身就是比较偏“函数式”的。
除了推崇显示输入、输出,即“无副作用”的写法;
它还推崇“值的不变性”。值的不变性就为了消除“状态”,函数式编程就强调“无状态”。
在大型项目中,每当声明一个新的变量,在多处去维护这个状态,这一定是一件容易导致混乱的事情。
再加上时间上的异步,乱上加乱,一层层去修改、覆盖值,刷新再刷新,很难再看清值变化的逻辑,还更加消耗性能。
函数式就有这个好:
用函数去运算值,而不更改值,函数组合就是值发生变化的过程。
函数式,再加响应式,消除时间状态,用事件流表达,极少的代码量就能实现复杂的功能。
只是,比如像 RxJS ,它的操作符比较复杂。可是像 React 的自定义 hooks 这种一样也是自定义方法,难道直接用不香?
可能二者并不矛盾,只是在往同样一个方向前进,其间有不同的表现。
说了这么多,归结一句话:
想要优雅的复用代码,务必学习函数式编程思想。你可能已经在用它了,而不自知。
专栏总结
突然,感觉没有太多想说的了,DDDD,借用延迟处理的思想:现在不想说,等想说的时候再说吧~~~
OK,以上便是本篇分享,专栏第 7 篇,希望各位工友喜欢~ 欢迎点赞、收藏、评论