函数的柯里化 , 自执行函数 , 防抖节流

145 阅读5分钟

函数的柯里化

在我们之前书写函数时,通常函数都是同时接收多个参数来实现功能,

但是在柯里化函数中,就是把这个函数拆分成多个函数,每个被拆分出来的函数接收一个或多个参数来实现功能,通常情况下,外层函数负责接收功能,内层函数负责接收剩余参数或者处理功能并返回结果

简单来说 : 函数柯里化就是我们给一个函数传入一部分参数,此时就会返回一个函数来接收剩余的参数。

1. 简单的柯里化实现

  • 普通函数
    function sum(a, b, c){
        return a + b + c
    }
    console.log(sum(1, 2, 3))  //6
  • 柯里化函数
  function sum(a){
    return function(b){
      return function(c){
        return a + b + c
      }
    }
   }
   console.log(sum(1)(2)(3))  //6

柯里化函数的封装

1.普通函数

        function fn(a, b, c, d) {
            return a + b + c + d
        }
        fn('我', '喜', '欢', 'js')
        fn('你', '不', '喜欢', 'js')   

上述的代码中只有一个函数,我们可以通过多次传参调用这个函数来实现功能

但是在函数式编程中,我们往往希望一个函数处理的问题尽可能的单一,而不是将一大堆的过程交给一个函数来处理。要符合设计模式中的单一职责原则

因此我们可以将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果。

所以在这个时候封装后的柯里化函数就比较符合我们的要求了

2.柯里化函数封装版

        function fn(a, b, c, d) {
            // console.log(fn.length)//4
            return a + b + c + d
        }
        
        function curry(callback, ...arg) {  // 外层函数: 负责接收参数
            /**
             *  curry 函数需要接受两个参数
             *      callback: 当参数传递足够时需要执行的函数   => 指向fn
             *      ...arg: 接收后续这个参数传递所有实参 (以数组的形式存储),加三个点视为了接受后续所有参数
            */
            return function (..._arg) {    // 内层函数: 负责处理功能
                // _arg = [arg, _arg]//两个都是数组,为了方便维护,用es6的展开运算符
                _arg = [...arg, ..._arg]

                // if ('当前接收参数的数量' === 'fn函数需要的参数数量') {
                //     '执行 fn 函数'
                // } else {
                //     '此时参数数量不满足函数要求, 需要继续收集参数'
                // }
                // _arg.length === fn  //不能直接写fn,这样就写死了,看看fn给了哪个形参,使用形参来代替fn
                //新知识点: 函数.length  可以拿到函数参数的数量
                if (_arg.length === callback.length) {
                    //不能写fn(),这样就写死了,而且参数必须写,不然输出就是undefined
                    return callback(..._arg)//参数以数组的形式存在,用展开符展开
                } else {
                    // return curry()
                    //不能直接仅仅调用curry,这样的话之前已经传进来的参数就白传了
                    return curry(callback, ..._arg)
                }
            }
        }
        //直接传递全部参数
        let newFn = curry( fn, '我', '喜', '欢', 'js')
        console.log(newFn())    //我喜欢js
        
        //分批传递参数
        let newFn = curry(fn, '你')
        const newFn2 = newFn('不', '喜欢')
        const newFn3 = newFn2('js')
        console.log(newFn3)  //你不喜欢js

自执行函数

实际上不同的人对自执行函数的理解不太一样,第一种理解是,自执行即自动执行,也就是大家平时所谓的立即执行函数。

还有一种理解,即自执行函数是在函数内部执行函数本身,即我们平时常说的递归函数

但是不管是哪种理解,我们都没必要去纠结叫法上的准确与否,我们更重要的是要明白自执行函数怎么使用, 下面我们结合代码来讲解一下

首先,我们来书写一个普通的函数

    function fn(){
      console.log('我是普通的函数fn')
    }
    fn()  //我是普通的函数fn

通过代码我们可以以看出,想要执行fn函数,我们需要手动的去调用,

下面我们来感受一下自执行函数

    /**    
     * 加小扩号把上面代码包起来,并且在最后面再加一个小扩号,最前面加一个分号
     *    
     * 自执行函数需要在代码最前边添加一个  分号  ,用于和上一行代码起到一个分割作用
     * 如果前面没有需要区分的代码,也可以不加分号
     *    
     * 自执行函数如果需要传参,将实参书写在第二个小括号内即可
    */
    
    ;(function fn(num){
      console.log('我是自执行的函数fn',num)
    })(99)
    //输出结果为 : 我是自执行的函数fn, 99

在自执行函数中,我们无需手动去执行函数,函数会自动执行并打印

函数的防抖

防抖 就是指事件在执行时,第一次开始执行时,在结束之前或者指定时间之前,无法触发下一次 除非等到第一次执行结束或者在指定时间到达后,才进行下一次

1.我们以输入框为例讲解防抖

    <input type="text" class="inp">
    const inp = document.querySelector('.inp')
    
    inp.oninput = (function (flag) {
    //当前这个函数会立即执行然后返回f一个函数给到inp.oninput
      return function () {
        //使用 flag 的时候会先在当前作用域找。没找到。
        //然后去上层作用城(自执行函数内)找,在这里找到了形参flag,初始值为true
        if (flag === false) return
        flag = false
        setTimeout(() => {
          flag = true
          console.log(this.value)
        }, 300);
      }
    })(true)

函数的节流

防抖 事件在开始执行时,如果快速触发第二次,那么第二次会取代第一次,只会执行最后一次

同样以输入框为例

  <input type="text" class="inp">
    const inp = document.querySelector('.inp')
    
    inp.oninput = (function (timer) {
      return function () {
        clearInterval(timer)

        timer = setTimeout(() => {
          console.log(this.value)
        }, 300);
      }
    })(0)