文章目录
JS底层运行机制:
EC/AO/VO/GO/SCOPE/SCOPE-CHAIN
我们编写的JS代码都是执行在一个环境里的,例如:
- 浏览器(引擎)
- node(基于v8渲染js)
- webview(v8引擎)
- Execution Context Stack
执行环境栈,栈内存(从内存中分配出的一个) - EC Execution Context 执行上下文
- AO Active Object 私有对象
- VO Variable Object 变量对象
- GO Global Object 全局对象
- SCOPE 作用域:创建函数的时候就赋予的
- SCOPE-CHAIN 作用链域
ECStack
浏览器想要执行js代码,需要提供一个代码执行环境ECStack
EC
在编程语言中,代码执行时,为了区分全局和函数执行所处的不同作用域(目的是为了区分每个词法作用域下代码的独立性)而创建出的执行上下文
VO - AO
- 每一个上下文代码执行的时候,可能都会创建变量,所以在每一个上下文中都会有一个存储变量的空间VO
- 变量对象:存放当前上下文中的变量
- 全局称为VO(G)
- 私有上下文中称为AO(XXX),也是变量对象
GO
浏览器把所有后期需要供JS调用的属性和方法都放置在GO对象中
并且在全局中创建一个window变量指向这个GO对象
堆(Heap)栈(Stack)内存及垃圾回收机制
内存堆栈问题
let a = {n: 1};
let b = a;
a.x = a = {n: 2};
console.log(a.x);
console.log(b);
- 注意.x的优先级,开辟出
{n:2}
的堆空间,a.x指向这个新的堆空间 - 然后a的指针改变,指向新的堆空间
- 所以
a.x
为undefined, b指向原来的堆空间
函数的创建与执行
创建一个函数
- 开辟一个堆内存(16进制的内存地址:AAAFFF111)
- 声明当前函数的作用域(在哪个上下文中创建的,它的作用域就是谁)
- 把函数体中的代码当做“字符串”存储在堆内存中(创建一个函数,存储的是一堆字符串,所以函数只要不执行,函数其实没啥意义)
- 把函数堆的地址类似于对象一样,放置在栈中供变量调用(函数名)
执行函数
-
每一个函数执行会形成一个全新的私有上下文 EC(xx)(目的是供函数体中的代码执行),然后进栈执行
-
在私有上下文中有一个存放私有变量的变量对象 AO(xx)
-
在代码执行之前要做的事情很多:
- 初始化它的作用域链 <自己上的上下文,函数的作用域>
- 初始化THIS (箭头函数没有THIS)
- 初始化ARGUMENTS实参集合(箭头函数没有ARGUMENTS)
- 形参赋值(形参变量是函数的私有变量,需要存储在AO中的)
- 变量提升(在私有上下文中声明的变量都是私有变量)
…
-
代码执行(把之前在函数堆中存储的字符串,拿过来在上下文中依次执行)
-
根据实际的情况确定当前上下文是否出栈释放
-
为了保证栈内存的大小(内存优化),一般情况下,如果当前函数执行产生的上下文,在进栈且代码执行完成后,会把次上下文移除栈(上下文释放到了:之前在上下文中存储的私有的变量等信息也就有跟着释放了) =>全局上下文是在打开页面生成的,也需要在关闭页面的时候释放掉(只有页面关闭才会被释放掉)
-
特殊情况:只要当前上下文中的某些内容,被上下文以外的东西占用,那么当前上下文是不能被释放的(上下文中存储的变量等信息也保留下来了) =>这种情况就是大家认为的闭包
-
作用域链查找机制:
- 在代码执行中,遇到一个变量,我们首先看一下是否为自己的私有变量,如果是自己的私有变量,接下来所有操作都是操作私有的(和外界没有直接的联系)
- 如果不是自己私有的,则按照scope-chain,向上级上下文中查找(如果是上级私有的,接下来的操作都是操作上级上下文中的变量)…一直找,直到找到EC(G)为止
浏览器的垃圾回收机制
栈内存(执行上下文)
- 一般情况下,函数执行完,所形成的上下文会被出栈释放掉
- 特殊情况:当前上下文中某些内容被上下文以外的事物占用了,此时不能出栈释放
- 全局上下文:加载页面创建的,也只是有页面关闭才会被释放掉
堆内存:浏览器的垃圾回收机制
- 引用计数(以IE为主):在某些情况下会导致计数混乱,这样会造成内存不能被释放(内存泄漏)
- 检测引用(占用)(以谷歌为主):浏览器在空闲时候会依次检测所有的堆内存,把没有被任何事物占用的内存释放掉,以此来优化内存
手动释放管理内存
其实就是解除占用(将外界的引用手动赋值为null即可)
题目
题一
var x = 1;
function func(x, y = function anonymous1() {x = 2}) {
x = 3;
y();
console.log(x);
}
func(5);
console.log(x);
分析题中上下文
- 全局执行上下文EC(G)
- 私有执行上下文
- EC(func).
作用链域:<EC(func), EC(G)>
- EC(anonymous1)
作用 链域:<EC(anonymous1), EC(func)>
- EC(func).
解析
EC(func)
中的x = 3;
影响EC(func)
中的形参xEC(anonymous1)
中的x=2
影响EC(func)
中的形参x,因为EC(anonymous1)
并没有x,所以打印2- 最后一个log打印
EC(G)
中的x = 1
题二
var x = 1;
function func(x, y = function anonymous1() {x = 2}) {
var x = 3;
y();
console.log(x);
}
func(5);
console.log(x);
- 注意一点:
y()
不属于与var x = 3
同级的块级上下文EC(BLOCK)
- 他是声明在
EC(FUNC)
里的 - 所以通过作用域链
<EC(anonymous1), EC(FUNC)>
可得x = 2
修改的是func
中的形参x