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

94 阅读4分钟

1.沙箱模式

利用了 函数内 "间接" 的返回了一个函数

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

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
let winA = res.getA()

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

console.log(res.getA())

案例

<button class="add1">+</button>
<input type="text" class="inp1" value="1">
<button class="sub1">-</button>
// 0. 获取标签
const add1 = document.querySelector('.add1')
const inp1 = document.querySelector('.inp1')
const sub1 = document.querySelector('.sub1')

// 通过沙箱模式创建变量
function outter() {
    let count = 1

    return {
        getCount() {
            return count
        },
        setCount(val) {
            count = val
        }
    }
}

const res = outter()

add1.onclick = function () {
    // 增加
    let num = res.getCount()
    res.setCount(num + 1)
    inp1.value = res.getCount()
}
sub1.onclick = function () {
    // 减少
    let num = res.getCount()
    res.setCount(num - 1)
    inp1.value = res.getCount()
}

// 正常版写法
let count = 1
add1.onclick = function () {
    // 增加
    count++
    inp1.value = count
}
sub1.onclick = function () {
    // 减少
    count--
    inp1.value = count
}

沙箱模式语法糖

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

// 普通版
function outter() {
    let a = 1

    return {
        getA () {
            return a
        },
        setA (val) {
            a = val
        }
    }
}

const res = outter()

res.setA(99)
console.log(res.getA()) // 99
//语法糖版, 其实就是利用 getter 和 setter 帮助我们简化了一些操作与代码
function outter() {
    let a = 1

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

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

2.函数的柯里化

1.柯里化函数:

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

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

// 基础版
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(/^\w{6,8}$/, 'qwer')
testStr(/^\d{5,8}$/, '12345')
testStr(/^\d{5,8}$/, '54321')
testStr(/^\d{5,8}$/, '124567890')
testStr(/^\d{5,8}$/, '98765')
testStr(/^\d{5,8}$/, 'qwertyui')
//柯里化函数
function fn(fnReg) { // 外层函数, 负责接收正则
    return function (fnStr) { // 内层函数, 负责接收字符串, 并完成正则校验
        // console.log(fnReg.test(fnStr)) // 只有 console.log 才会向控制台打印内容
        return fnReg.test(fnStr) // 将校验结果返回出去给外部代码使用
    }
}

const testNum = fn(/^\d{5,8}$/)
testNum('1234')
testNum('4321')
testNum('123')

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

2. 函数的柯里化封装

函数柯里化内

  • 外层函数: 收集参数
  • 内层函数: 负责处理功能

功能: 拼接地址栏字符串

www.baidu.com/index.html

www.baidu.com/a.html

传输协议: http https

域名: 127.0.0.1 localhost

端口号: 0~65535 7777 8080 8081

地址: /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) {
    return a + '://' + b + ':' + c + d
}
function curry(callback, ...arg) {  // 外层函数: 负责接收参数
    /**
     *  curry 函数需要接受两个参数
     *      callback: 当参数传递足够时需要执行的函数
     *      ...arg: 接收后续这个参数传递所有实参 (以数组的形式存储)
    */

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

        // if ('当前接收参数的数量' === 'fn函数需要的参数数量') {  函数名.length => 就是这个函数形参的数量
        //     '执行 fn 函数'
        // } else {
        //     '此时参数数量不满足函数要求, 需要继续收集参数'
        // }

        if (_arg.length === callback.length) {
            return callback(..._arg)
        } else {
            return curry(callback, ..._arg)
        }
    }
}
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)

3.函数的节流防抖

1.节流

  • 事件在执行时, 第一次开始执行时, 在结束之前或者在指定时间之前, 无法触发下一次
  • 除非等到第一次执行结束或者在指定时间到达后, 才可以进行下一次
<input type="text" class="inp">
//获取元素
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')
}
/**
*  自执行函数需要在代码最前边添加一个 分号, 用于和上一行代码起到一个分割作用
* 
*  自执行函数如果需要传参, 将实参书写在 第二个小括号内即可
*/
;(function fn(num) {
    console.log('我是一个普通函数 fn', num)
})(99)
// 2. 节流的优化
// let flag = true


inp.oninput = (function (flag) {    // 当前这个函数会立即执行, 然后返回一个 函数给到 inp.oninput

    return function () {
        // 使用 flag 的时候会先在 当前作用域找, 没找到, 然后去上层作用域(自执行函数内)找, 在这里找到了 形参 flag, 初始值为 true
        if (flag === false) return
        flag = false
        setTimeout(() => {
            flag = true
            console.log(this.value)
        }, 300)
    }

})(true)

2.防抖

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


// 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)