Appearance
堆内存释放机制
如果当前堆内存的16进制地址,被其他事物所引用,则堆内存不能释放掉(强引用) 如果当前堆内存不被其它东西所占用,浏览器会在空闲的时候查找每一个内存的引用情况, 那些未被引用都会被回收释放。
js
const o = {} // 堆内存不会被释放,因为被o给占用了
o = null // 释放栈内存释放机制
全局上下文是加载页面的时候产生的,只有刷新/关闭页面的时候才释放(刷新是销毁后又开辟一个) 私有上下文(函数/块)一般情况下执行完都会出栈释放掉,因此来优化栈内存,但是有特殊情况, 如果当前上下文中创建的某个东西(一般都是堆内存)被上下文以外的其他事物给占用了, 那么当前上下文不能被释放 特点 1.消耗内存(慎用) 2.由于不被释放,私有上下文中的东西都被保留下来了,以后可以拿来用
js
function fn () {
return function () {
}
}
const f = fn() // 这是闭包,return的地址值被f占用,不能释放
function fn () {
document.body.onclick = function () {
}
}
fn() // 这也是闭包,fn上下文中的堆内存地址被占用,不能释放
function fn () {}
fn() // 私有上下文执行完销毁闭包:函数执行产生一个全新的私有上下文,并且这个私有上下文不能被释放掉 1.保护:保护里面的私有变量不受外界的干扰,防止全局变量污染 2.保存:私有上下文不被释放,里面的私有变量和值就保存起来了,可以供下级上下文使用
闭包应用: 单例模式 柯里化 组合函数 惰性函数
js
// example1
let x = 5
const fn = function fn (x) {
return function (y) {
console.log(y + (++x))
}
}
let f = fn(6)
f(7)
fn(8)(9)
f(10)
console.log(x)
// example1
var i = 5
function fn (i) {
return function (n) {
console.log(n + (++i))
}
}
var f = fn(1)
f(2)
fn(3)(4)
fn(5)(6)
f(7)
console.log(i)
// example2
var i = 20
function fn () {
i -= 2
return function (n) {
console.log((++i) - n)
}
}
var f = fn()
f(1)
f(2)
fn()(3)
fn()(4)
f(5)
console.log(i)js
// example2
let a = 0,
b = 0
function A(a) {
A = function (b) { // 函数重构 原来的私有上下文中的地址值被A(A是上下文外面的东西)占用,上下文不会被释放
console.log(a + b++)
}
console.log(a++)
}
A(1)
A(2)
// example
function fn (i) {
return function (n) {
console.log(n + (i++))
}
}
var f = fn(10)
f(20)
fn(20)(40)
fn(30)(50)
f(30)
// example
var num = 10
var obj = { num: 20 }
obj.fn = (function (num) {
this.num = num * 3
num++
return function (n) {
this.num += n
num++
console.log(num)
}
})(obj.num)
var fn = obj.fn
fn(5)
obj.fn(10)
console.log(num, obj.num)
// example
function fun(n, o) {
console.log(o)
return {
fun: function (m) {
return fun(m, n);
}
};
}
var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);
c.fun(2);js
// example3
// 下面代码是否可以每隔1000ms依次输出0 1 2 3 4
// 如果不可以,是为什么?如何解决
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, (i + 1) * 1000)
}
// 不行,i是全局变量,定时器回调执行的时候循环已经结束了,i = 5
// 方法1
for (var i = 0; i < 5; i++) {
(function (i) {
// 闭包,定时器回调被window引用
setTimeout(function () {
console.log(i)
}, (i + 1) * 1000)
})(i)
}
// 方法2
for (var i = 0; i < 5; i++) {
// 大函数是自执行,所以能接收到参数,执行上下文产生私有i
// 同时小函数被window引用,形成闭包,上级上下文是自执行的上下文
setTimeout((function (i) {
return function () {
console.log(i)
}
})(i), (i + 1) * 1000)
}
// 等价于上面
var fn = function (i) {
return function () {
console.log(i)
}
}
for (var i = 0; i < 5; i++) {
// 大函数是自执行,所以能接收到参数,执行上下文产生私有i
// 同时小函数被window引用,形成闭包,上级上下文是自执行的上下文
setTimeout(fn(i), (i + 1) * 1000)
}
// 方法3
// 创建长度为5的数组并把其变为密集数组,这样才能forEach
new Array(5).fill(null).forEach((_, index) => {
// 小函数被window引用,闭包
setTimeout(function () {
console.log(index)
}, (index + 1) * 1000)
})
// 方法4 私有块级上下文
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, (i + 1) * 1000)
}
// 方法5 终极方案 定时器第三个及以后的参数都是传给回调的实参
// 原理:定时器回调定义好了,把实参先存储在一个其他位置中 => 核心原理也是闭包
// 时间到了后把回调执行,形参为之前预先存储起来的值
for (var i = 0; i < 5; i++) {
setTimeout(function (i) {
console.log(i)
}, (i + 1) * 1000, i)
}