小丸子函数式编程初探
1. question
前一个项目,顺利完成的时候感觉特别棒。但是后面需求方迭代加需求的时候,甚至代码重构的时候,感觉特别乏力。
当时说不出来问题在哪,但是感觉自己写的代码维护成本很高,加需求的时候需要四处去改。
考虑一下,大概有以下几个方面做的不够好:
- 组件设计
项目的复杂组件在所选的技术栈上并没有例子( nej + regular)。所以不得已,复杂组件都是自己手写定制的。项目周期又比较紧,以至于,有一两个组件,特别复杂,其实并没有封装的好,而且是同事在我的组件基础上继续改变成另一个组件。这个时候组件设计的问题就暴露出来了。很多逻辑并没有抽离好,当后面组件越来越庞大的时候,手忙脚乱。
有一句话说的很好,
数据结构设计的不合理,这个组件就已经失败了(原话我忘了,这是我翻译的,是这个意思。)
- 编程方式
最近尝试使用组内大佬(他是函数式编程小王子)编写的一堆高阶函数,用它们来简化一些常用函数和操作。感觉特别棒。
甚至震惊我的是:有一个页面,我预估一天可以写完,但是我尝试用大佬的高阶函数封装的一套数据与操作绑定的小框架(我词穷,就当小框架吧)来实现的时候,我一小时就把页面写完了,而且代码量很小很干净。
顿时感觉自己之前写的代码low爆了。
- 回调地狱
目前项目各种使用promise
了。
有时间深入研究。
本文研究一下关于编程方式:函数式编程。
下面带大家看看不同的编程方式,对于项目的开发及其维护,多么重要吧。
2. 函数式编程
1. 命令式编程 vs 函数式编程
先看一个例子吧。
<html>
<head>
<title>test</title>
</head>
<body>
<div id="switcher" class="on"></div>
<script>
const switcher = document.querySelector('#switcher');
function changeColor(evt) {
const target = evt.target;
if (target.className === 'on') {
target.className = 'off';
} else {
target.className = 'on';
}
}
switcher.onclick = changeColor;
</script>
<style>
#switcher {
width: 100px;
height: 100px;
border: solid 1px white;
border-radius: 50px;
}
.on {
background-color: green;
}
.off {
background-color: red;
}
</style>
</body>
</html>
这段代码实现的效果是页面上有一盏红绿灯,每点击一次,就进行一次红/绿切换。
核心代码是这个函数。
function changeColor(evt) {
const target = evt.target;
if (target.className === 'on') {
target.className = 'off';
} else {
target.className = 'on';
}
}
我上一个项目,大量充斥着这样的代码,if``else
满天飞。
现在是红灯绿灯切换,如果加一个黄灯,又要加一堆if``else
。是不是维护成本挺高的。
用函数式的思维来实现是这个样子的:
const switcher = document.querySelector('#switcher');
function toggleQueue(...actions) {
return (...args) => {
const act = actions.shift();
actions.push(act);
return act.apply(this, args);
}
}
switcher.onclick = toggleQueue(
// evt => evt.target.className = 'yellow',
evt => evt.target.className = 'off',
evt => evt.target.className = 'on'
);
需要黄灯只需要加一句evt => evt.target.className = 'yellow'
就可以了。
2. 高阶函数
输入是函数或者输出是函数的函数。(这也是我翻译的,是这个意思)
3. 实用小栗子
本文几个函数式编程的小例子,都是项目中最常用的功能,希望给函数式编程小白一些启发。
// 函数节流
function throttle(fn, timer = 500) {
let time;
return (...args) => {
if (!time) {
fn.apply(this, args);
time = setTimeout(() => {
time = null;
}, timer);
}
}
}
function call() {
console.log('hello');
}
switcher.onclick = throttle(call);
// 只生效一次
function once(fn) {
return (...args) => {
if (fn) {
fn.apply(this, args);
fn = null;
}
}
}
function call() {
console.log('hello');
}
switcher.onclick = once(call);
// 链式调用
function chain(fn) {
return (...args) => {
fn.apply(null, args);
return this;
}
}
function call() {
console.log('hello');
}
function write() {
console.log('write');
}
call = chain(call);
write = chain(write);
call().write();
// 批量处理
function pack(map) {
return (obj) => {
for(let key in obj) {
map[key].call(this, obj[key]);
}
}
}
function call(name) {
console.log('call ' + name);
}
function write(name) {
console.log('write ' + name);
}
let css = pack({call: call, write: write})
css({call: 'hello', write: 'world'});
我的理解是,函数式的设计就是把一个个功能剥离出来,实现为高阶函数,给最基础的函数附加上一系列的功能。
参考:
http://www.zcfy.cc/article/javascript-and-functional-programming-an-introduction