闭包:函数执行,形成私有上下文,来保存和保护私有变量的机制,称之为闭包=>它是一种机制
我们用一道题来分析
例1:
let x = 1;
function A(y){
let x = 2;
function B(z){
console.log(x+y+z);
}
return B;
}
let C = A(2);
C(3);
1.首先创建了EC Stack 执行环境栈,并且创建了一个供全局代码执行的EC(G)执行环境上下文,并且进栈执行
2.在里面形成GO全局变量以及VO(G)全局变量对象
回顾:
GO:全局对象window 在堆内存中,里面放置的是浏览器内置的API
VO(G):全局变量对象,在上下文中的空间,里面放置的是上下文中创建的变量
二者不想等,但是有关联:
基于var/function在全局上下问中声明的全局变量也会给GO赋值一份(映射机制)
但是就let/const等ES6方式在全局上下文中创建的全局变量和GO没有关系
3.然后就是代码部分了
首先进行变量提升,function A(){},同时也会映射到window中一份
由于老版本浏览器带function的会声明+定义,所以就确定了其作用域,也会创建一个堆内存,同时确定了形参"y",用来存放函数的属性和方法,并且会有一个16进制的地址存放在栈内存当中供别人a指针指向
堆内存中包含:函数内容的字符串以及名字、形参个数、原型....

开始执行代码
1.let x = 1;在栈内存中创建一个变量x和值1,并把它们关联起来
let/const等ES6方式不会映射给全局GO
2.遇到function A(y){},由于变量提升,已经声明定义过了,所以略过
3.let C = A(2);先把函数A执行,把执行完返回的结果赋值给C
函数A执行前的事情
1.函数每执行一次就创建一个堆内存,也会形成一个私有上下文EC(A),里面会有私有变量对象AO(A)
2.初始化作用域链:<EC(A),EC(G)>
初始化this
初始化arguments:[0:2]
形参赋值 y = 2
变量提升 function B(z){},还是像上面函数A一样,创建一个堆内存...
开始执行函数A
1.let x = 2;在栈内存中创建一个变量x和值1,并把它们关联起来
2.遇到function B(z){},由于变量提升,已经声明定义过了,所以略过
3.return B;把私有变量B的值作为返回值返回(B的堆内存地址)
4.此时全局变量C的值为B的地址

1.形成的私有上下文
2.进栈执行
3.一系列操作(代码执行前和代码执行)
4.正常情况下
代码执行完,私有上下文会出栈(出栈后被释放,以此解约栈内存的空间);
但是有特殊情况:如果当前私有上下文中的某个东西(一般是一个堆)被上下文以外的事物占用了,则上下文不会再出栈释放,也就是形成不销毁的上下文;
不能销毁的话,就会被一直放在栈内存底部全局上下文中的上面,如果还有没被销毁的上下文则在栈内存中继续往上摞
浏览器的垃圾回收机制(浏览器自己内部处理)
[谷歌等浏览器是“基于引用查找“来进行垃圾回收的]
1. 开辟的堆内存,浏览器自己默认会在空闲的时候,查找所有内存的引用,把那些不被引用的内存释放掉
2. 开辟的栈内存(上下文)一般在代码执行完都会出栈释放,如果遇到上下文中的东西被外部占用,则不会释放
[IE等浏览器是“基于计数器”机制来进行内存管理的]
1. 创建的内存被引用一次,则计数1,在被引用一次,计数2... 移除引用减去1... 当减为零的时候,浏览器会把内存释放掉
=>真实项目中,某些情况导致计数规则会出现一些问题,造成很多内存不能被释放掉,产生“内存泄漏”;查找引用的方式如果形成相互引用,也会导致“内存泄漏“
比如:let aa = {name:"哈哈"}
let pp = aa
那么此时对象就不会释放
怎么让才能让其释放呢?
aa = null;
pp = null;
null代表空对象指针,是不会占用内存的,
回到上题
开始执行C(3)
与函数A执行钱一样,进行一系列的操作
执行代码的时候,遇到变量先看是否是自己私有的,如果不是私有的按照作用域链向上查找,不是上级的再往上查找,直到EC(G)为止
x为EC(A)的x 2
y为EC(A)的y 2
z自己私有的 3
所以结果为 7

如果想让EC(A)释放
C = null;
总结:
函数执行会形成全新的私有上下文,这个上下文可能被释放,也可能不被释放,不论是否被释放,它的作用是:
- 保护:划分一个独立的代码执行区域,在这个区域中有自己私有变量存储的空间,而用到的私有变量和其它区域中的变量不会有任何的冲突(防止全局变量污染)
- 保存:如果上下文不被销毁,那么存储的私有变量的值也不会被销毁,可以被其下级上下文中调取使用
我们把函数执行,形成私有上下文,来保存和保护私有变量的机制,称之为“闭包” =>它是一种机制
可以理解为:其实函数本身就是一种闭包,只是存在时间太短了就被释放了
市面上一般认为只有形成的私有上下文不被释放,才算是闭包(因为如果一但释放,之前的东西也就不存在了);还有人认为,只有一下级上下文用到了此上下文中的动西才算闭包;