二:JS中的堆栈内存与函数的创建和执行

首先说明一下下文中所用到的各种词语:
ECStack(Execution [ˌeksɪˈkjuːʃn] Context Stack和EC(Execution Context )执行环境栈
GO(Global Object)全局对象
VO(Varibale Object)全局变量对象
AO(Activation Object)私有变量对象

浏览器想要执行代码,需要提供一个供代码执行的环境:
ECStack(Execution Context Stack) 执行环境栈 =>也就是栈内存(全局上下文) 在这里有一个全局对象(GO) 浏览器把所有提供JS使用的属性和方法(内置,如setInterval JSON )都放在GO中 并且在全局用一个变量window指向他
js中的创建变量与赋值:
①如果创建的变量是一个值类型->直接存在当前上下文中(也就是说没有单独分配内存存储)
②如果创建的变量是一个引用数据类型->先开辟一个堆内存,然后创建的变量引用该堆内存的地址

let a = 10 ; //没有开辟新内存 直接将值赋值给a
let a = {name:'xxx'} // 分配了一个新的堆内存 赋值给a的是一个堆内存地址

函数的创建与执行:
1.创建一个函数:
①开辟一个堆内存
②声明当前函数的作用域(在哪个上下文中创建的,它的作用域就是谁)
③把函数体中的代码当做“字符串”存储在堆内存中(创建一个函数,存储的是一堆字符串,所以函数只要不执行,函数其实没啥意义)
④把函数堆的地址放置在栈中供变量调用(函数名)
2.函数执行:
①会形成一个全新的私有上下文 EC(xx)(目的是供函数体中的代码执行),然后上下文会进栈执行
②在私有上下文中有一个存放私有变量的变量对象 AO(xx) 全局上下文中的变量对象叫VO(G)
③在代码执行之前要做的事情很多:
初始化它的作用域链 <自己上的上下文,函数的作用域>
初始化THIS (箭头函数没有THIS)
初始化ARGUMENTS实参集合(箭头函数没有ARGUMENTS)
形参赋值(形参变量是函数的私有变量,需要存储在AO中的)
变量提升(在私有上下文中声明的变量都是私有变量)
④代码执行:
把之前在函数堆中存储的字符串,拿过来在上下文中依次执行
作用域链查找机制:在代码执行中,遇到一个变量,我们首先看一下是否为自己的私有变量,如果是自己的私有变量,接下来所有操作都是操作私有的(和外界没有直接的联系);如果不是自己私有的,则按照scope-chain,向上级上下文中查找(如果是上级私有的,接下来的操作都是操作上级上下文中的变量)....一直找,直到找到EC(G)为止
⑤根据实际的情况确定当前上下文是否出栈释放:
为了保证栈内存的大小(内存优化),一般情况下,如果当前函数执行产生的上下文,在进栈且代码执行完成后,会把次上下文移除栈(上下文释放到了:之前在上下文中存储的私有的变量等信息也就有跟着释放了) =>全局上下文是在打开页面生成的,也需要在关闭页面的时候释放掉(只有页面关闭才会被释放掉)
特殊情况:只要当前上下文中的某些内容,被上下文以外的东西占用,那么当前上下文是不能被释放的(上下文中存储的变量等信息也保留下来了)

注意!函数第二次执行,会形成一个全新的私有上下文,把之前做过的事情,还是原封不动的再执行一次(所有的东西都是从头来一遍的),此时形成的上下文和上一次形成的上下文之间没有必然的联系

一般情况下,函数执行完,所形成的上下文会被出栈释放掉
特殊情况:当前上下文中某些内容被上下文以外的事物占用了,此时不能
出栈释放
全局上下文:加载页面创建的,也只是有页面关闭才会被释放掉

浏览器的垃圾回收机制:
1.引用计数(以IE为主):在某些情况下会导致计数混乱,这样会造成内存不能被释放(内存泄漏)
2.检测引用(占用)(以谷歌为主):浏览器在空闲时候会依次检测所有的堆内存,把没有被任何事物占用的内存释放掉,以此来优化内存

你可能感兴趣的:(二:JS中的堆栈内存与函数的创建和执行)