沙箱模式、函数的柯里化、函数的防抖节流

93 阅读4分钟

一、沙箱模式

  • 利用了 函数内 “间接” 的返回了一个函数

  • 外部函数返回了一个对象,这个对象内书写多个函数

    function outer() {
      //1.放置一些外部函数的私有变量
      let a = 0
      let b = 999

      // 2.对象内部放置若干个函数
      const obj = {
        getA: function () {
          return a
        },
        setA: function (val) {
          a = val
        },
        getB() {
          return b
        },
        setB(val) {
          b = val
        }
      }

      // 3.将对象返回到函数外
      return obj
    }

    //得到函数内部的对象,这个对象内部有多个操作函数
    const res = outer()

    // 得到函数内部的私有变量 a
    const winA = res.getA()

    // 修改函数内部的私有变量 a
    res.setA(winA + 1)
    console.log(res.getA())

二、沙箱模式语法糖

语法糖:在不影响功能的情况下,给我们提供了一些更简单的操作方式

  • 其实就是利用 gettersetter 帮助我们简化了一些操作与代码
    // 语法糖版
    function outer() {
      let a = 1

      return {
        get a() {
          return a
        },
        set a(val) {
          a = val
        }
      }
    }

    const res = outer()
    console.log(res.a)
    res.a = 999
    console.log(res.a)


    // 普通版沙箱模式
    // function outer() {
    //   let a = 1
    //   return {
    //     getA() {
    //       return a
    //     },
    //     setA(val) {
    //       a = val
    //     }
    //   }
    // }
    // const res = outer()

    // res.setA(99)
    // console.log(res.getA()) //99

三、函数的柯里化

柯里化函数:一个函数接收多个参数能够完成功能,柯里化函数就是将这一个函数拆分成两个函数,每个函数都只能接收一个参数,然后完成的功能相同。

    //需求:使用正则完成一个文本匹配

    // 基础版
    // const reg = /^\w{6, 12}$/
    // const str = '12345'
    // console.log(reg.test(str))

    // 优化版
    // function testStr(fnReg, fnStr) {
    //   const reg = fnReg
    //   const str = fnStr
    //   console.log(reg.test(str))
    // }
    // testStr(/^\d{5,8}$/, '12345')
    // testStr(/^\d{5,8}$/, '54321')
    // testStr(/^\d{5,8}$/, 'qwer')

    //柯里化函数
    function fn(fnReg) {  //外层函数,负责接收正则
      return function (fnStr) { //内层函数,负责接收字符串,并完成正则校验
        console.log(fnReg.test(fnStr)) //只有console.log才会向控制台打印内容
        return fnReg.test(fnStr) //将校验结果返回出去给外部代码使用
      }
    }
    const testNum = fn(/^\d{5,8}$/)
    // console.log(testNum)

    testNum('1234')
    testNum('4321')
    testNum('123')

    const testName = fn(/^\w{6,12}$/)
    testName('qwertyui')

四、函数的柯里化封装

函数柯里化内:

  • 外层函数:收集参数

  • 内层函数:负责处理功能

  • 功能:拼接地址栏字符串

www.baidu.com/index.html

www.baidu.com/a.html

传输协议: http https

域名: 127.0.0.1 localhost

端口号:0~65535 7777 808

地址: /a /a/b.html /index.html

普通版

    // 普通版
    // function fn(a, b, c, d) {
    //   return a + '://' + b + ':' + c + d
    // }

    // fn('http', '127.10.11.12', '8080', '/index.html')
    // fn('http', '127.10.11.12', '7777', '/index.html')
    // fn('http', '127.10.11.12', '7777', '/a.html')
    // fn('http', '127.10.11.12', '7777', '/b.html')
    // fn('http', '127.10.11.12', '7777', '/c.html')

柯里化版

    // 柯里化版
    function fn(a, b, c, d) {
      // console.log(fn.length)
      return a + '://' + b + ':' + c + d
    }
    // fn()
    function curry(callback, ...arg) { //外层函数:负责接收参数
      // console.log('callback', callback)
      // console.log('arg', arg)
      /**
       * ?curry 函数需要接收两个参数
       * ?   callback : 当参数传递足够时需要执行的参数
       * ?   arg: 接收后续这个参数传递的所有实参(以数组的形式存储)
      */

      return function (..._arg) { //内层函数:负责处理功能
        _arg = [...arg, ..._arg] //把所有参数放在一个数组中,方便统一维护与管理

        // if('当前接收参数的数量' === 'fn函数需要的参数数量'){
        //   '执行fn函数'
        // }
        // else{
        //   '此时参数 数量不满足函数要求,需要继续收集参数'
        // }

        if (_arg.length === callback.length) {
          return callback(..._arg)
        }
        else {
          return curry(callback, ..._arg)
        }
      }
    }
    // let newFn = curry(fn, 'http', '127.0.0.1', '8080', '/index.html')
    // console.log(newFn())

    // let newFn = curry(fn, 'http') 
    // const newFn2 = newFn('127.0.01', '8080')
    // const newFn3 = newFn('/index.html')
    // console.log(newFn2)

    const newFn = curry(fn)
    const newFn2 = newFn('https')
    const newFn3 = newFn2('127.0.0.1')
    const newFn4 = newFn3('7777')
    const newFn5 = newFn4('/index/html')
    console.log(newFn5)

五、函数的防抖节流

1.节流

  • 事件在执行时,第一次开始执行时,在结束之前或者在指定时间之前,无法触发下一次

  • 除非等到第一次执行结束,或者在指定时间到达后,才可以进行下一次

html

<input type="text" class="inp">

JS

    const inp = document.querySelector('.inp')

    // 0.基础版
    // inp.oninput = function () {
    //   console.log(this.value)
    // }

    // 1.节流
    // let flag = true
    // inp.oninput = function () {

    //   if (flag === false) return

    //   flag = false

    //   setTimeout(() => {
    //     flag = true
    //     console.log(this.value)
    //   }, 300)
    // }

    // 2.扩展 :自执行函数
    // function fn() {
    //   console.log('我是一个普通函数fn')
    // }
    // fn()

    // ; (function fn(num) {
    //   console.log('我是一个普通函数fn', num)
    // })(99)

    // 2.节流的优化
    // let flag = true
    inp.oninput = (function (flag) { //当前这个函数会立即执行,然后返回一个函数给到inp.oninput
      // console.log(flagF) //true
      return function fn() {
        //使用flag的时候会先在当前作用域找,没找到,然后去上层作用域(自执行函数内)找,在这里找到了形参flag,初始值为true
        if (flag === false) return
        flag = false
        setTimeout(() => {
          flag = true
          console.log(this.value)
        }, 300)
      }
    })(true)

2.防抖

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

html

<input type="text" class="inp">

JS

    const inp = document.querySelector('.inp')

    // 0.基础版
    // inp.oninput = function () {
    //   console.log(this.value)
    // }

    // 2.防抖
    // let timer = 0
    // inp.oninput = function () {
    //   clearInterval(timer)

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

    //3.自执行函数
    // let timer = 0
    inp.oninput = (function (timer) {
      return function () {
        clearInterval(timer)
        
        timer = setTimeout(() => {
          console.log(this.value)
        }, 300)
      }
    })(0)

扩展:自执行函数

  • 自执行函数需要在代码最前面添加一个分号,用于和上一行代码起到一个分割作用

  • 自执行函数如果需要传参,将实参书写在第二个小括号内即可