当浏览器打开一个页面时,从计算机的虚拟内存中分配两块内存,每打开一个网页都会生成一个全新的ECS。
- 栈内存 ECStack
- 堆内存 Heap
栈内存
- 存储原始值类型值
- 供代码执行
堆内存
存储对象值
- 默认在堆内存开辟一个空间(16进制地址)->GO全局对象。存储浏览器为js提供的内置API (setTimeout)
- 在栈中创建全局执行上下文(EC(G))
- 全局变量对象VO(G):存储声明的变量
- 在全局上下文中会默认创建一个变量window,让其指向GO的地址,所以可以window.XX去访问浏览器内置的API
定义变量defined
例如 let value = 12
- 先创建值12
- 原始值:直接存储在栈中
- 对象:在堆中重新开辟一块空间,把地址赋值给变量
- 声明变量a
- 把12赋值给a(建立指针指向)
let a = {n:1}
a.x = a = {n:2}
// 正常从右到左计算,特殊情况:优先级高的先处理(成员访问)
//a.x = {n:2} a = {n:2}
全局上下文中(只在全局上下文中有这个特点)
- 基于var/function声明的变量,并没有放在VO(G)-script,直接存储在GO(global)中,作为window的成员
- 基于let/const声明的变量存储在VO(G)中
- c = 14 不加声明关键词放在GO(G)全局对象
- 当把一个对象赋值给变量时,只是把地址赋值给变量。如果修改对象值,变量里的值也会跟着变化
let a = {n:1} // a->ox001堆内存地址
let b = a //b->ox001
a.n = 2
console.log(b) // b值地址指向不变
//{n: 2}
函数执行上下文
通过变量环境(var)与词法环境(let/const)存储声明的变量
函数底层处理机制
- *在工作中尽量用函数表达式声明函数,防止函数提升以及在未声明前使用。
- *函数是可执行对象,既有函数特点含有对象特点
创建函数
- 开辟一个堆内存空间(16进制地址)
- 创建函数时就声明了其作用域,在哪个上下文创建的,其作用域就是谁 [[scope]]:EC(G)
- 把函数体中的代码当作字符串存储
- 普通对象name函数名,length(形参个数)
- prototype 原型对象
- __proto__原型链
- 把空间地址赋值给变量(函数名)
执行函数
- 创建一个全新的私有上下文EC(fn)->函数执行上下文
- AO(私有变量对象)
- 代码执行之前
- 初始化作用域链《函数私有上下文-函数作用域》
- 初始化this
- 初始化arguments
- 形参赋值(私有变量-》Ao)
- 变量提升
- 代码执行
- 关于上下文的回收释放 一般情况下,函数执行完,所形成的执行上下文会被释放。
作用域链
规划出变量的查找过程
- 私有上下文中遇到一个变量,首先看是否时自己私有的,如果是,接下来对于对变量的操作都是私有的,和外界无关,{保护起来}
- 如果不是私有,基于作用域链去上级上下文中找,知道找到EC(G)为止。