函数节流
概念
规定一个单位时间,在单位时间内,只有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次效
实现方法
- 利用
延时器+ 闭包
/**
* 函数节流 (延时器 + 闭包)
* @fn 回调函数
* @delay 延时秒数
* */
function throttle(fn, delay){
let time ;
return function(){
if(time){
return
}
time = setTimeout(()=>{
fn.apply(this, arguments) ; // 将闭包返回的函数,this指给回调函数(fn); arguments 参数传递给回调函数
time = null ; //节流参数,和return配合,限制代码继续执行
}, delay)
}
}
- 利用
时间戳+ 闭包
/**
* 函数节流 (延时器 + 闭包)
* @fn 回调函数
* @delay 延时秒数
* */
function throttle(fn, delay){
let temp = null , timer;
return function () {
let now = +new Date() ; //获取当前时间戳,实时变化的
clearTimeout(timer);
if(temp && now > temp + delay){ // 当前时间戳 > 闭包时间戳 + 延时时间
fn.apply(this, arguments) ; //同上
temp = now; //节流
} else {
temp = now ; //获取第一次加载的时间戳(缓存到闭包当中), + : 将日期格式转换为时间戳
timer = setTimeout(()=>{
fn.apply() ;
}, delay)
}
}
}
使用: xxx.addEventListener( 'input', throttle() ) ; 这里throttle运行返回一个函数(闭包的使用理由)
函数防抖
概念
其实就是从机械开关和继电器的“去弹跳”(debounce)衍生 出来的,基本思路就是把多个信号合并为一个信号。
通俗来讲:触发某个函数,执行某个动作,多个函数对应一个动作。
实现方法
- 利用
延时器,延时器内相当于 动作,多次触发函数执行动作,当函数不断触发,动作就不断打开关闭,直到函数停止,动作才执行。
/**
* 函数防抖
* */
function debounce(fn, time) {
let timer = null ;
return function(){
clearTimeout(timer) ; //关闭上一个动作
timer = setTimeout( ()=> { //直到函数不在触发, clearTimeout 不在执行, 动作才能执行
fn.apply(this, arguments) ;
}, time)
}
}
函数节流和函数防抖结合
函数防抖常用于滚动 scroll 事件、 input事件, 但是 函数防抖是用户停止输入时,才执行动作。如果用户一直滚动或输入 ,动作将不会执行 , 所以加入函数节流,到某个时间段,执行动作。
/**
* 函数防抖、节流
*/
function debounceThrottle(fn, delay){
let timer, temp = +new Date();
return function () {
let now = +new Date() ;
if(now > temp + delay ){
fn.apply(this, arguments) ;
temp = now ;
console.log("节流运行")
} else {
clearTimeout(timer) ;
timer = setTimeout(()=>{
fn.apply(this, arguments) ;
console.log("定时器运行")
}, delay) ;
}
}
}
高阶函数
函数柯里化、反柯里化都属于高阶函数,什么叫 (高阶函数)。
满足一下两点:
1. 函数可以被当参数传递
2. 函数可以作为返回值输出
柯里化
概念
提前接受部分参数,延迟执行,不立即输入结果,而是返回一个接受剩余参数的函数
实现方法
// 实现 add(1,2)(3,5)() = 11
/**
* 函数柯里化
* */
function add(){
let allArgs = [ ...arguments] ; //缓存第一次数据
return function next(...args){
if(args.length){
allArgs = [ ...allArgs, ...args] ; //缓存合并剩余参数
return next //如果还有参数,继续缓存合并参数
}
let result = 0; //计算接受所有参数的总和,知道所有参数接受完毕 再运行。
for(let i = 0, len = allArgs.length; i < len; i++){
result += allArgs[i] ;
}
return result
}
}
add(1,3)() = 4
运用的技术及难点
- 闭包,使
allArgs一直放在缓存中,不被释放,保持原来的值 . - 当传入参数时,函数先不运行,而是缓存
(记忆)起来. - 当无参数时,说明参数接受完毕,运行程序计算最终结果。
- 使用ES6 解构, 比如: [ ...arguments ] 将
伪数组变为真正数组,[ ...allArgs, ...args]合并数组
反柯里化
概念
反柯里化,将柯里化过后的函数反转回来,由原先的接受单个参数的几个调用 转变 为接受多个参数的单个调用
一种简单的实现方法是:将多个参数一次性传给柯里化的函数,因为我们的柯里化函数本身就支持多个参数的传入处理,反柯里化调用时,仅使用“一次调用”即可。
// 反柯里化
function uncurry(fn) {
var args = [].slice.call(arguments, 1);
return function() {
var arg = [].slice.call(arguments);
args = args.concat(arg);
return fn.apply(this, args);
}
}
var uncurryAdd = uncurry(add);
console.log(uncurryAdd(1, 2, 3, 4)()); // 10
var uncurryMul = uncurry(add, 2);
console.log(uncurryMul(3, 4)()); // 24