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. 函数的柯里化封装
函数柯里化内
- 外层函数: 收集参数
- 内层函数: 负责处理功能
功能: 拼接地址栏字符串
传输协议: 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)