浏览器底层运行机制:上下文,作用域以及堆栈内存

JS底层运行机制:

EC/AO/VO/GO/SCOPE/SCOPE-CHAIN

我们编写的JS代码都是执行在一个环境里的,例如:

  1. 浏览器(引擎)
  2. node(基于v8渲染js)
  3. 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指向原来的堆空间
    在这里插入图片描述

函数的创建与执行

创建一个函数

  1. 开辟一个堆内存(16进制的内存地址:AAAFFF111)
  2. 声明当前函数的作用域(在哪个上下文中创建的,它的作用域就是谁)
  3. 把函数体中的代码当做“字符串”存储在堆内存中(创建一个函数,存储的是一堆字符串,所以函数只要不执行,函数其实没啥意义)
  4. 把函数堆的地址类似于对象一样,放置在栈中供变量调用(函数名)

执行函数

  1. 每一个函数执行会形成一个全新的私有上下文 EC(xx)(目的是供函数体中的代码执行),然后进栈执行

  2. 在私有上下文中有一个存放私有变量的变量对象 AO(xx)

  3. 在代码执行之前要做的事情很多:

    1. 初始化它的作用域链 <自己上的上下文,函数的作用域>
    2. 初始化THIS (箭头函数没有THIS)
    3. 初始化ARGUMENTS实参集合(箭头函数没有ARGUMENTS)
    4. 形参赋值(形参变量是函数的私有变量,需要存储在AO中的)
    5. 变量提升(在私有上下文中声明的变量都是私有变量)
  4. 代码执行(把之前在函数堆中存储的字符串,拿过来在上下文中依次执行)

  5. 根据实际的情况确定当前上下文是否出栈释放

    1. 为了保证栈内存的大小(内存优化),一般情况下,如果当前函数执行产生的上下文,在进栈且代码执行完成后,会把次上下文移除栈(上下文释放到了:之前在上下文中存储的私有的变量等信息也就有跟着释放了) =>全局上下文是在打开页面生成的,也需要在关闭页面的时候释放掉(只有页面关闭才会被释放掉)

    2. 特殊情况:只要当前上下文中的某些内容,被上下文以外的东西占用,那么当前上下文是不能被释放的(上下文中存储的变量等信息也保留下来了) =>这种情况就是大家认为的闭包

作用域链查找机制:

  • 在代码执行中,遇到一个变量,我们首先看一下是否为自己的私有变量,如果是自己的私有变量,接下来所有操作都是操作私有的(和外界没有直接的联系)
  • 如果不是自己私有的,则按照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);

在这里插入图片描述
分析题中上下文

  1. 全局执行上下文EC(G)
  2. 私有执行上下文
    1. EC(func).
      作用链域:<EC(func), EC(G)>
    2. EC(anonymous1)
      作用 链域:<EC(anonymous1), EC(func)>

解析

  1. EC(func)中的x = 3;影响EC(func)中的形参x
  2. EC(anonymous1)中的x=2影响 EC(func)中的形参x,因为EC(anonymous1)并没有x,所以打印2
  3. 最后一个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

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值