这是我参与「第四届青训营 」笔记创作活动的第 6 天。(第五篇笔记)
本篇笔记再次仍然延续上一节的内容,继续为「如何写好 JavaScript 」,从「过程抽象」的角度出发。
过程抽象
过程抽象是:
- 用来处理局部细节控制的一些方法;
- 函数式编程思想的基础应用。
过程抽象的理解: 可以将函数看作是一个输入和输出的黑盒,在黑盒(过程)之中可以做函数过程本身的一些抽象,实现一些通用型的方案。
将过程抽象想象成一个房间,房间中的门、窗和房间空间本身都是数据,要完成「开门」和「关门」的动作或者行为本身是一个过程,也就是说不仅要将数据进行抽象,「开门」这个过程也可以抽象。
🌰 例子 / 操作次数的限制:
codepen button list (codepen.io)
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);
})
})
这段代码:点击列表中的按钮后,该列表项目会在 2s 后移出该项目节点。但是如果在移除节点之前,又点击了几次按钮,此时会报错(DOM 节点不存在)。
原因:在 2s 之内再次点击按钮,DOM 元素还存在,还会触发定时器执行 removeChild,但是元素只能移除一次,引起了报错。
解决:限制这个事件的执行次数,让它只能在第一次触发的时候执行。抽象出一个高阶函数 once ,该函数的参数为一个函数,返回值也为一个函数,在返回的函数中判断传入的函数是否存在,如果存在则执行该函数,执行完后将该函数赋值为 null ,这样下次传入的函数不存在就不会再次执行了。
function once(fn) {
return function (...args) {
if (fn) {
const ret = fn.apply(this, args);
fn = null;
return ret;
}
};
}
const list = document.querySelector("ul");
const buttons = list.querySelectorAll("button");
buttons.forEach((button) => {
button.addEventListener(
"click",
once((evt) => {
const target = evt.target;
target.parentNode.className = "completed";
setTimeout(() => {
list.removeChild(target.parentNode);
}, 2000);
})
);
});
在这个例子中,只执行一次需要覆盖不同的事件处理,过程抽象就是将 只执行一次 的需求抽离出来。
高阶函数
高阶函数的定义:
- 以函数为参数;
- 以函数作为返回值;
- 常用于函数装饰器;
🌰 例子:
function HOF(fn) {
return function(...args) {
return fn.apply(this, args)
}
}
常用的高阶函数有:
once只执行一次函数throttle节流函数debounce防抖函数consumeriterative
为什么要使用高阶函数(如何理解高阶函数 - 掘金 (juejin.cn))
高阶函数把 函数当作变量。
纯函数
纯函数的定义:如果一个函数的返回结果只依赖他的参数,并且执行过程没有副作用则称为纯函数。
使用纯函数非常可靠,并且不会对外界产生影响。再多人开发中能够方便进行单元测试。减少系统中非纯函数的属性,从而使得系统可靠性增加。
总结
到目前为止(「如何写好 JavaScript」这个主题),已经接触了写好 JavaScript 的三个原则:
- 各司其职
- 组件封装(封装性、正确性、拓展性、复用性)
- 过程抽象(函数式编程思想)