闭包
不会销毁的函数执行空间
在学习闭包之前,可以先了解一下基本和数据类型和引用数据类型的存储
- 当函数返回了一个 复杂数据类型
- 并且函数外部有变量接受这个 复杂数据类型
- 函数执行完毕后就不会销毁
function fn() {
const obj = { name: 'tom', age: '18' }
return obj
}
let res = fn()
res = 100
代码小结:
fn 函数执行完毕后,return 返回一个对象的地址,res 接受的就是这个地址,如果不需要这个空间 ,只需要res变量指向别的位置就可以。这里res赋值了100,原来的空间已经销毁了
认识闭包
三个条件:
-
需要一个不会被销毁的函数执行空间
-
需要 直接 或者 间接 返回一个函数
-
内部函数 使用 外部函数的 私有变量
概念 : 函数内的函数 特点 1. 可以在函数外面访问到函数内部的变量 2. 延长了变量的生命周期 3. 内存溢出
浏览器的垃圾回收机制 : 计数 和 标记
当变量进入环境,要么计数要么给个标记, 如果计数为0, 或者标记为离开环境,垃圾回收机制进行回收,如果无法回收,形成内存溢出
function outer() {
let a = 100
let b = 200
// inner 是 outer的闭包函数
function inner() {
// console.log(a);
return a
}
return inner
}
// console.log(outer());
// outer()()
const res = outer()
console.log(res);
// 在outer外面使用了 outer里面的变量
const r1 = res()
console.log(r1);
代码小结:
这里我们使用函数里面嵌套函数,在调用outer时返回一个函数inner,这个inner使用变量res装起来,这样延长了变量的生命周期。
沙箱模式
利用闭包
利用函数内间接返回 一个对象
外部函数返回一个对象, 对象内书写多个函数
function outer() {
let a = 100
let b = 200
// 创建一个对象 --- 沙箱
const obj = {
getA: function () {
return a
},
getB() {
return b
},
setA(num) {
a = num
}
}
return obj
}
// const res = outer() // obj
// console.log(res.getA());
// console.log(res.getB());
// res.setA(1000)
// console.log(res.getA());
// -------------------------
// 思考
const res = outer()
res.setA(1000)
console.log(res.getA()); // 1000
const res1 = outer()
console.log(res1.getA()); // 100
沙箱语法糖
尽可能简化沙箱模式的语法
利用 getter 和 setter 来进行操作
操作的时候, 对象.设置(获取) 名称
function outer() {
let a = 100
let b = 200
const obj = {
// get关键字 getter获取器,用来获取成员a
get a() {
return a
},
get b() {
return b
},
set a(num) {
a = num
}
}
return obj
}
const res = outer()
console.log(res.a);
console.log(res.b);
// 设置变量a
res.a = 1000
console.log(res.a);
函数柯理化
把一次传递两个参数,变成两次,每次传递一个参数
利用了闭包, 把每一次传递的参数保存下来(延长变量的生命周期)
案例一: 求和
// 求和 求 10+num 的和
function fn(num) {
return 10 + num
}
// 求和 求 20 + num
function fn1(num) {
return 20 + num
}
function curry(init) {
return function (num) {
return init + num
}
}
const fn10 = curry(10)
fn10
fn10(20)
console.log(fn10(20));
const res = curry(10)(20)
const res = curry(30)(20)
console.log(res);
const res = curry(20)
///10000行代码
res(30)
案例二: 正则验证用户名和密码
// 方案1
const reg = /^\w{6,12}$/
const res = reg.test('asdfasdfadsf')
// 把正则验证用户名封装起来
// 方案1
function testName(str) {
const reg = /^\w{6,12}$/
return reg.test(str)
}
const res = testName('asdfasdf')
const res1 = testName('asdf23423')
// 封装正则验证密码
function testPwd(str) {
const reg = /^\w{8,10}$/
return reg.test(str)
}
// 方案2
// function testReg(reg, str) {
// return reg.test(str)
// }
// // // 书写一个验证密码
// const res = testReg(/^\w{8,10}$/, 'adfasdf')
// // 书写一个验证用户名
// const res1 = testReg(/^\w{6,12}$/, 'adsfasdf')
// const res2 = testReg(/^\w{6,12}$/, 'ads334')
// const res3 = testReg(/^\w{6,12}$/, 'rtuyrtuy')
// // 利用 柯理化(闭包) 封装
function test(reg) {
return function (str) {
return reg.test(str)
}
}
// 生成一个验证密码的函数
const testPwd = test(/^\w{8,10}$/)
// 生成一个验证用户名的函数
const testName = test(/^\w{6,12}$/)
// 生成一个验证手机号的函数
const testPhone = test(/^1\d{10}$/)
/// ============================
// 想要校验 用户名
const res = testName('asdfasdfas')
// 校验手机号
const res1 = testPhone('234234234234')
函数柯理化封装
- 是多个参数互相利用
- 需要两个功能
功能函数
收集参数
功能
拼接 地址栏
http://localhost:8080/a/b
a : http
b : localhost
c : 8080
d : /a/b
功能函数 把 a b c d 拼接起来
// 柯理化函数
function curry(fn, ...arg) {
// 收集参数,判断参数的个数,够不够功能函数使用
// 够了 执行功能函数
// 如果 不够 ,继续收集
// console.log('功能函数', fn);
// console.log('收集的参数', ...arg);
// 对外部收集的参数进行处理
let _arg = arg || []
// console.log(_arg);
// 需要用_arg的length和 功能函数的参数个数进行比对
// 语法 获取函数形参的个数
// 函数名.length
let len = fn.length
console.log(len);
return function (...arg) {
_arg = [..._arg, ...arg]
// 用处理好的参数进行数量判断
if (_arg.length === len) {
// 处理好的参数 ==== 功能函数参数相同
return fn(..._arg)
} else {
// 处理好的参数<功能函数参数
return curry(fn, ..._arg)
}
}
}
// const res = curry(fn)
// 情况1
const res = curry(fn, 'http')
const res1 = res('localhost')
const res2 = res1('8080')
const res3 = res2('/a/b') // http://localhost:8080/a/b
console.log(res3);
// 情况2
// const res = curry(fn, 'http', 'localhost')
// const res1 = res('8080', '/a/b') // // http://localhost:8080/a/b
// 情况3
// const res = curry(fn, 'http', 'localhost', '8080', '/a/b')() // // http://localhost:8080/a/b
// console.log(res);