过程抽象
[源码]:code.juejin.cn/pen/7108192…
- 应用函数式编程思想(编程范式)
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
把function本身看作是一个封装好的过程,关注输入和输出。
-
eg : 多次调用但是只执行一次
-
创建一个按钮,按钮的功能是移除一条li。setTimeout为2秒。
-
当点击按钮的时候,li已经被移除了,但是由于还有2s的渐退时间,会导致按钮在这两秒之内还存在,li不存在的情况。
-
我们点击按钮多次,但是删除只能执行一次,一次之后就没有能删除的东西了。(所以就会报错)
-
const list = document.querySelector('ul');
const buttons = list.querySelectorAll('button');
buttons.forEach((button) => {
button.addEventListener('click', (evt) => {
const target = evt.target;
target.parentNode.className = 'completed';
setTimeout(() => {
list.removeChild(target.parentNode);
}, 2000);
});
});
-
第一种解决办法:
我们可以在监听按钮click事件的时候,加入{once : true},这样多次点击按钮之后就不会报错了。
button.addEventListener('click', (evt) => { const target = evt.target; target.parentNode.className = 'completed'; setTimeout(() => { list.removeChild(target.parentNode); }, 2000); },{once:true}); -
第二种解决办法:
我们可以把过程封装成once过程抽象(高阶函数)
once高阶函数保证内部函数只执行一次。
const foo = once(() => {
console.log('bar');
});
foo();
foo();
foo(); // 调用三次但是只在控制台输出一个bar
once返回一个function,如果一个函数返回另一个函数,那么这个函数叫做高阶函数。
function once(fn) {
// outer scope
return function(...args) {
// inner scope
if(fn) {
const ret = fn.apply(this, args);
fn = null;
return ret;
}
}
}
once 函数的目的是返回一个新函数,这个新函数只能被调用一次。
-
输入:
once(fn)接受一个函数fn作为参数。
-
返回值:
once(fn)返回一个新的函数,这个新函数会在第一次调用时执行fn,并且之后不再执行fn。
-
内部逻辑:
- 新函数使用
fn.apply(this, args)调用原函数fn,并将参数args传递给它。 this在这一行是指原函数fn被调用时的上下文。- 调用后,
fn被设置为null,使得之后的调用没有任何作用,因为没有函数可以被执行。
- 新函数使用
-
执行过程:
- 第一次调用新函数时,
fn会被执行,并且执行后会将fn置为null。 - 第二次及之后的调用将不做任何事情。
- 第一次调用新函数时,
Once: 为了能够让“只执行一次”的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。
比如说开门,开冰箱,都是开,我们就能抽象出来 open。
在JS中,我们可以使用高阶函数来实现过程抽象。
-
高阶函数(HOF)
- 以函数作为参数
- 以函数作为返回值
- 常用于作为函数装饰器
-
常用高阶函数
-
Once
-
Throttle 节流函数 鼠标移动,页面滑动的时候会触发很多监听事件,我们可以使用节流函数,限制多长时间监听一次。
-
-
Debounce 防抖函数 要实现一个自动保存的功能,如果每次都保存,会对服务器造成一个很大的负担,当我编辑完成之后,键盘不敲的时候,再帮我进行自动保存。超过多少毫秒没有动,再进行自动保存
[源码] code.juejin.cn/pen/7108193…
注:等待
dur毫秒后调用fn函数。setTimeout的作用是设置一个定时器,等待指定的时间dur毫秒后再执行一个函数。
-
Consumer 把一个同步的函数变成一个异步的函数,延时调用的效果
[源码] code.juejin.cn/pen/7108194…
- Iterative 迭代器
[源码] code.juejin.cn/pen/7108194…
-
为什么要使用高阶函数?
一般来说,高阶函数都是纯函数。
-
纯函数:没有副作用,并且结果是可预测的。
// add是一个纯函数,我们可以知道结果是否符合预期 function add(x,y){ return x + y; } let idx = 0; // inpure function with side effect 测试成本高,越多系统可维护性越差 function count(){ return ++idx; } count() count() const result = count() // 如果我们进行对count函数的测试,会受到其上文的影响, // 所以在对该函数进行测试时, // 需要先 init/setup()前面环境初始化, // 然后teardown()结束的时候也需要把环境销毁掉,因为要测试别的内容。 console.assert(result === 3,'failed ${result}'); // 显然是一个非纯函数,因为会改变el的状态 function setColor(el,color){ el.style.color = color; } setColor(app,'red'); // 也是一个非纯函数 function setColors(els,color){ els.forEach(el) =>{ setColor(el,color); } } // 如果对上述两个代码都进行测试的话,都需要写test case。如果用Iterative -
可以大大减少使用非纯函数的可能性。
-
-
编程范式:
- 命令式:面向过程,面向对象,怎么做
- 声明式:描述的是想要的结果,而不需要指定具体的步骤或执行过程,做什么
命令式:code.juejin.cn/pen/7108195…
用到了 if else
声明式:code.juejin.cn/pen/7108195…
shift() 是从actions里面取出来,push是放到最后一个。也就是取出来现在的,放在最后一个。
写成声明式的话,添加或者减少都很简单。
声明式比命令式语言天然的具有更强的可扩展性。