一、函数创建与定义的过程
- 函数定义阶段
- 在堆内存中开辟一段空间
- 把函数体内的代码一模一样的的存储在这段空间内
- 将这个空间的地址, 赋值给函数名(栈内存中)
- 函数调用阶段
- 按照变量名内的存储地址找到堆内存中对应的存储空间
- 在调用栈中开启一个空间 (我们叫做执行空间)
- 在执行空间中进行形参赋值
- 在执行空间中进行预解析
- 在执行空间中完整执行一遍函数内的代码
- 开辟的函数执行空间销毁
二、不会销毁的函数执行空间
- 当函数内返回一个复杂数据类型
- 并且函数外部有变量接收这个复杂数据类型
- 函数执行完毕的函数执行空间不会被销毁
- 如果以后不需要这个执行空间了, 那么只需要将外部的变量更改一个引用地址就行
function fn() {
const obj = {
a: 1,
b: 2
}
return obj
}
const res = fn()
console.log(res)
res = 100
三、认识闭包
- 需要一个不会被销毁的函数执行空间
- 需要 直接 或 间接的返回一个函数
- 内部函数, 需要访问外部函数内创建的局部变量
- 优点:
- 可以在函数外面访问到函数内部的变量
- 延长了变量的生命周期
- 缺点
- 每一个闭包都会创建一个不会销毁的内存空间,如果闭包书写的太多, 那么这个不会被销毁的空间就越来越多,就可能会造成页面/程序的卡顿,大量使用会造成内存溢出
function outer () {
let a = 100
let b = 200
function inner () {
return a
}
return inner
}
let res = outer()
console.log(res)
console.log(res())
四、沙箱模式
- 是 JS 中 利用 闭包 完成的一个设计模式
- 设计模式: 为了解决某一类问题的最优化的写法, 但不是万能
- 利用了 函数内 "间接" 返回一个函数
- 外部函数 返回一个对象, 对象内书写多个函数
function outter() {
let a = 100
let b = 99
const obj = {
getA() {
return a
},
setA(val) {
a = val
},
getB() {
return b
},
setB(val) {
b = val
}
}
return obj
}
const res_1 = outter()
console.log(res_1.getA())
res_1.setA(999)
console.log(res_1.getA())
const res_2 = outter()
console.log(res_2.getA())
1、沙箱模式小案例
<button class="sub">-</button>
<input class="inp" type="text" value="1">
<button class="add">+</button>
<br>
<button class="sub1">-</button>
<input class="inp1" type="text" value="1">
<button class="add1">+</button>
function outer() {
let a = 1
return {
getA() {
return a
},
setA(val) {
a = val
}
}
}
const subBtn = document.querySelector('.sub')
const addBtn = document.querySelector('.add')
const inp = document.querySelector('.inp')
let res = outer()
subBtn.onclick = function () {
let count = res.getA()
res.setA(count - 1)
inp.value = res.getA()
}
addBtn.onclick = function () {
let count = res.getA()
res.setA(count + 1)
inp.value = res.getA()
}
const subBtn1 = document.querySelector('.sub1')
const addBtn1 = document.querySelector('.add1')
const inp1 = document.querySelector('.inp1')
let res1 = outer()
subBtn1.onclick = function () {
let count = res1.getA()
res1.setA(count - 1)
inp1.value = res1.getA()
}
addBtn1.onclick = function () {
let count = res1.getA()
res1.setA(count + 1)
inp1.value = res1.getA()
}
五、沙箱模式的语法糖
- 尽可能的简化沙箱模式的语法
- 利用的是 getter 和 setter 来进行操作数据
- 语法糖:
- 再不影响功能的情况下, 提供一点更适合操作的语法
- 一碗水, 喝了能让我们补充水分, 水里加点糖, 喝完能让我们补充水分, 并且更好喝了
function fn1() {
let a = 100
return {
getA () {
return a
},
setA (val) {
a = val
}
}
}
const res1 = fn1()
console.log(res1.getA())
res1.setA(999)
console.log(res1.getA())
function fn2() {
let a = 1
let b = 'QF001'
return {
get a () { return a },
set a (val) { a = val },
get b () { return b }
}
}
const res2 = fn2()
console.log(res2.a)
res2.a = 666
console.log(res2.a)
console.log(res2.b)
res2.b = 'QF999'
console.log(res2.b)
六、闭包的面试题
function fun(n, o) {
console.log(o)
const obj = {
fun: function (m) {
return fun(m, n)
}
}
return obj
}
var a = fun(0)
a.fun(1)
a.fun(2)
a.fun(3)