闭包
1.闭包的本质
在函数外部,调用操作函数,函数内部的数据。
为了保护数据的安全性
全局变量容易造成全局变量污染
为了确保数的安全性,会将重要的数据,定义为局部变量
再通过闭包的形式,调用使用函数的内部数据。
2.函数的深入了解
2.1. 封装
(1)在 内存的 堆 中 开辟一个存储空间
操作系统给这个存储空间赋值内存地址
存储空间准备存储 函数程序
(2)函数程序 以 字符串形式 存储在存储空间中
(3) 函数名称 / 变量名称 存储在 内存的 栈 中
(4)函数名称 / 变量名称 中 存储的是 函数的内存地址
2.2 调用执行
(1)通过 栈 中的 函数名称 / 变量名称 中 存储的内存地址
找到 堆 中的 存储空间 调用其中存储的函数的代码程序
(2)给 函数的形参 赋值 实参
(3) 预解析 / 预编译 / 预解释 函数的程序
(4) 执行 函数程序代码
2.3 函数执行开始
在 堆 中 函数的存储空间中
再 开辟一个独立的 内存空间 称为 执行空间
专门 存储 函数执行时 需要的 形参 变量 等
2.4 函数执行结束
执行空间自动 销毁 / 释放
执行空间中存储的 形参 变量 等 也会一起 销毁 / 释放
这个过程被称为 JavaScript 内存回收机制 / 内存释放机制 / 内存管理机制……
3 闭包的基本原理1
不能调用 函数内部数据的原因是
函数执行结束 执行空间就会被释放 其中存储的 形参和变量 也会被释放
如果 要 操作调用 函数内部的 形参和变量
就 需要 执行空间 不会被 销毁/释放
生成 一个 不会被 销毁/释放 的 执行空间 原理
函数的返回值 是一个 引用数据类型
function fun1(){
let int = 100 ;
return int ;
}
// res1 中 存储的是 函数fun1 的 执行结果返回值
// 也就是 return 的 变量int中 存储的 数据数值 100
let res1 = fun1();
function fun2(){
const arr = [100,200,300,400];
return arr ;
}
// res2 中 存储的是 函数fun2 的 执行结果返回值
// 也就是 return 变量arr中 存储的 数组的内存地址
// 数组arr 的 内存地址 在 函数外 被 变量存储
// 也就是 数组arr 在 函数外 被 使用
// 此时 函数的执行空间 不会被 销毁/释放
// 但是 此时 还不能直接 操作 变量arr
const res2 = fun2();
3 闭包的基本原理2
/*
1, 定义一个函数A
在这个函数A中 定义的 形参/变量
是 实际程序中 全局变量
为了 防止全局变量污染
定义成 函数内的 形参/变量 也就是 局部变量
2, 函数A 的 return返回值 是一个 匿名函数
这个 匿名函数 操作调用 函数A中
被保护的 全局变量
3, 在 函数A 外 调用函数A 并且使用 变量储存 函数A的调用结果
变量中存储的是 函数A的执行结果返回值
也就是 return 的 匿名函数
4, 调用执行变量 也就是在 调用执行 函数A return的返回值 匿名函数
也就是 在 操作调用 函数A 中 被保护的全局变量
总结:
为什么必须要写成return匿名函数的语法形式
如果多次 直接调用函数
每次 变量 都会被 重新定义重新赋值 永远是原始数值
如果多次 通过 return的匿名函数 操作 变量
变量只有第一次会被 调用执行一次
之后每次都 只是 操作变量
没有 再次调用 整个函数 也就是没有 再次重新定义变量 重新赋值
每次调用的数据 都是 不同的数值数据
*/
这里我们来看一个例子
// 定义一个函数 存储 变量int赋值100
function fun(){
// 数据定义为函数内部 不会被全局变量污染 数据就安全了
let int = 2 ;
// 函数的返回值 是一个 匿名函数
// 因为 return 的是一个 引用数据类型
// 执行空间 就不会被 销毁/释放 变量int 和 return的匿名函数就会一直存在
return function(){
// 匿名函数的程序内容 是 操作调用 写在函数内的 全局变量
int *= 2 ;
if( int > 500 ){
console.log(111);
}else{
console.log(222);
}
}
}
// res 中 存储的是 函数fun的返回值 也就是 return的 匿名函数
const res = fun();
console.log( res );
// 调用 变量() 就是 执行调用 匿名函数
// 也就是 对 变量int 数据的操作调用
res();