JavaScript中的高阶函数是指以函数作为参数,以函数作为返回值的函数,其常用作函数装饰器,对一些函数进行封装或者是限制。常用的高阶函数主要有以下几种。
ONCE(只能执行一次)函数的实现
有些时候我们需要一些函数只执行一次,尤其是点击事件中会经常用到,我们可以实用高阶函数once来实现这个功能。这里我列出了一个具体的实用场景,可以直接粘贴在body标签中运行。
<button id="btn">按钮</button>
<script>
function once(fn) {
return function (...args) {
if(fn){
const ret = fn.apply(this, args)
fn = null
return ret
}
}
}
const btn = document.getElementById('btn')
const clickEvent = (e) => {
console.log('clickEvent', e)
}
btn.addEventListener('click', once(clickEvent))
</script>
由于闭包,once中的匿名函数里的fn变量不会被清除,即持久化保存,因此,只需要执行一次fn后,将其设置为null,即可防止fn的再次执行。
TIMES(只能执行指定次数)函数的实现
once方法限制某个过程只执行一次,我们可以扩展once的实现思路,实现一个高阶函数times(fn, count=1),让一个函数只能执行count次。
function times(fn, count=1) {
return function (...args) {
if(count > 0){
count --
return fn.apply(this, args)
}
}
}
依旧利用闭包,通过变量count,进行执行次数的限制。
THROTTLE(节流)函数的实现
throttle方法限定某个函数执行的频率,或者是最大按照某个间隔去执行。其原理是使用一个延时器,在传进来的函数执行的时候,将状态赋值给timer,到达time毫秒以后才将其置空,只有timer为空的时候才允许传进来的函数执行。
const throttle = (fn, time) => {
let timer
return function(...args){
if (!timer) {
fn.apply(this, args)
timer = setTimeout(() => timer = null, time)
}
}
}
DEBOUNCE(防抖)函数的实现
debounce方法限定某个方法在某个区间内连续执行,最后只会执行一次。原理是在每次执行函数之前先清空所有延时器,然后再重新延时,直到没有新的函数调用,到指定时长后,函数才会执行一次。
const Debounce = (fn, dur=100) => {
let timer
return function(...args){
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, dur)
}
}
ITERATIVE(迭代)函数的实现
Iterative方法消除了传入函数中,需要被处理参数的差异化。假设我们需要将某个元素,或者某一类元素的文本颜色设置为红色,我们就可以将一个元素或者包含若干个元素的数组传入函数中。
// 判断参数是否为可迭代对象
const isIterable = (object) => object !== null && typeof object[Symbol.iterator] === 'function'
function iterative(fn) {
return function (subject, ...args) {
if (isIterable(subject)) {
for(const obj in subject) fn.apply(this, [obj, ... args])
}
else
fn.apply(this, [subject, ,,,args])
}
}
CONSUMER函数的实现
Consumer函数会将需要立即连续执行的过程,按照指定的间隔,以此延迟执行。使用一个队列,先进先出,到一定间隔后才执行下一个过程。需要注意的是,当任务队列为空的时候,需要清除定时器。
function consumer(fn, time){
let timer;
let queue = []
return function (...args){
queue.push(fn.bind(this, ...args))
if (!timer) {
timer = setInterval(function(){
queue.shift().call(this)
if (queue.length === 0) {
clearInterval(timer)
timer = null
}
}, time);
}
}
}