JavaScript 编码原则之过程抽象
过程抽象
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
例子
这段代码是一个用于限制函数只能执行一次的工具函数 once,以及该函数的使用示例。
首先是 once 函数的定义:
function once(fn) {
return function(...args) {
if(fn) {
const ret = fn.apply(this, args);
fn = null;
return ret;
}
}
}
该函数接受一个函数作为参数 fn,然后返回一个新的函数。这个新函数可以接受任意数量的参数 ...args。当调用这个新函数时,它会执行传入的 fn 函数,并将 fn 设置为 null,以确保 fn 只能被执行一次。
接下来是示例的代码:
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);
}));
});
这部分代码首先使用 querySelector 获取页面中的 <ul> 元素,并使用 querySelectorAll 获取所有的 <button> 元素。然后通过 forEach 函数遍历每一个按钮,并为每个按钮添加一个 click 事件监听器。
事件监听器使用了 once 函数来包装一个回调函数 (evt) => { ... },该回调函数会在按钮被点击时执行。在回调函数中,首先获取到被点击的按钮的父元素,将它的 className 设置为 "completed",然后使用 setTimeout 函数延迟2秒后移除该父元素。
最后注释掉的部分是另一个使用 once 函数的示例,它定义了一个函数 foo,并尝试多次调用 foo 函数。由于 once 函数的限制,foo 实际上只会执行一次,后续的调用都不会有任何效果。
总结起来,这段代码实现了一个功能,通过 once 函数可以限制一个函数只能执行一次,而在示例中,它被用于在点击按钮后添加一些样式并延迟删除对应的元素。
操作次数限制
- 一些异步交互
- 一次性的HTTP请求
高阶函数
- 以函数作为参数
- 以函数作为返回值
- 常用于作为函数装饰器
为了能够让“只执行一次“的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为**过程抽象**。
HOF
function HOF0(fn) {
return function(...args) {
return fn.apply(this, args);
}
}
常用高阶函数
HOF
首先是
throttle函数的定义:function throttle(fn, time = 500) { let timer; return function(...args) { if (timer == null) { fn.apply(this, args); timer = setTimeout(() => { timer = null; }, time); } }; }该函数接受两个参数:
fn为需要节流的函数,time为节流的时间间隔,默认为 500 毫秒。函数内部定义了一个定时器变量timer,初始值为undefined。返回的是一个新的函数,该函数可以接受任意数量的参数
...args。在每次调用这个新函数时,会判断timer的值。如果timer为null或者undefined,则执行传入的fn函数并将timer设置为一个新的定时器。这样,在一段时间内重复调用这个新函数时,fn函数只会执行一次。接下来是示例的代码:
btn.onclick = throttle(function(e) { circle.innerHTML = parseInt(circle.innerHTML) + 1; circle.className = 'fade'; setTimeout(() => (circle.className = ''), 250); });这部分代码给一个按钮
btn添加了click事件的监听器,并且将throttle函数作为回调函数传入。当按钮被点击时,回调函数会执行。在回调函数中,首先将
circle元素的内部文本内容解析为数字,加1后重新赋值给circle.innerHTML,这样每次点击按钮时数字会自增。然后将
circle的className设置为'fade',为其添加一个淡入的过渡效果。使用setTimeout函数延迟 250 毫秒后,将circle.className设置为空字符串,以移除淡入效果。整个示例的作用是,当按钮被点击时,通过节流函数将点击事件限制在一个时间间隔内执行,以避免短时间内多次点击导致的过快响应。在回调函数中,对
circle元素做了一些操作,包括数字增加、添加过渡效果等。
思考和讨论
- 为什么要使用高阶函数?
了解纯函数和非纯函数:纯函数,是指一个函数的返回值结果只依赖于它的参数,并且在执行过程里面没有副作用。
- 纯函数方便进行单元测试;
- 减少系统中非纯函数的数量,从而使得系统可维护性增加。
编程范式
命令式与声明式
//命令式(怎么做?)
let list = [1, 2, 3, 4];
let mapl = [];
for(let i = 0; i < list.length; i++) {
mapl.push(list[i] * 2);
}
//声明式(做什么?)
let list = [1, 2, 3, 4];
const double = x => x * 2;
list.map(double);
例子
- Toggle - 命令式
- Toggle - 声明式
- Toggle - 三态 声明式一般更强调过程抽象,声明式天然地比命令式具有可扩展性
总结
- 过程抽象 / HOF高阶函数 / 装饰器
- 命令式 / 声明式