通俗易懂的函数节流、函数防抖,函数柯里化和反柯里化

851 阅读4分钟

函数节流

概念

规定一个单位时间,在单位时间内,只有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次效

实现方法

  • 利用延时器 + 闭包
   /**
    * 函数节流 (延时器 + 闭包)
    * @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