JS 之所以能够在浏览器中运行,是因为浏览器给 JS 提供了执行的环境 => 栈内存(Stack)
- ECStack(Execution Context Stack) => 执行环境栈
- 浏览器会在计算机内存中分配一块内存,专门用来供代码执行
- EC => 执行上下文,作为代码执行所在的环境;代码执行之前,当前上下文会先进栈(ECStack),然后执行
- 全局的执行上下文 EC(G)
- 函数中的代码会在一个单独的私有的执行上下文中处理
- 块级的执行上下文
- GO => 全局对象
- 浏览器把内置的一些属性和方法放到一个单独的内存中,堆内存(Heap)
- 浏览器会让 window 指向全局对象 GO
- VO(Varible Object) => 变量对象
- 在当前的上下文中,用来存放创建的变量和值的地方(每一个执行上下文中都会有一个自己的变量对象)
- 函数私有上下文中叫做AO(Activation Object)活动对象,也是变量对象
定义变量 (分为三步) => 所有的变量赋值都是指针的关联指向
- 创建一个值
- 基本类型值: 直接存储在 栈内存 (VO / AO) 中
- 引用类型值: 开辟一个堆内存,把东西存储进去,最后把堆的地址存在栈内存 (VO / AO) 中
- 对象的创建:
- 1.开辟一个堆内存
- 2.把键值对存储到堆中
- 3.堆内存的地址放到栈中,供变量调用
- 创建一个变量
- 让变量和值关联在一起
/*
* GO 全局对象: 是一个堆内存,存储的是浏览器内置的API属性方法,浏览器端的 window 指向了 GO
*
* 浏览器提供一个代码执行的环境 => 栈内存
* 执行环境栈 ECStack
* 1.供代码执行
* 2.存储基本类型值 / 堆内存地址 / 变量
* 3.JS 中存在多种作用域(全局,函数私有,块级私有),为了保证这种机制,代码执行之前,首先会形成自己的执行上下文,然后把上下文进栈,进栈后在当前上下文中去依次执行代码
*
* EC(G) 全局执行上下文 => 进栈执行
* 每一个执行上下文中一定存在一个空间,用来存储创建的变量,叫做全局变量对象
*
* VO(G) 全局变量对象
* 1.全局上下文中用来存储全局变量的空间,在某些情况下 VO(G) 会和 GO 中的东西有所关联(映射机制)
* 2.所有的赋值操作: (1)创建值 (2)创建变量 (3)变量和值指针关联
* 3.基本类型值直接存在栈内存中,引用值是先开一个堆,将对象的键值对存储到堆中,最后把堆的16进制地址放到栈中供变量调用
*
* => 创建 AAAFFF000 堆; 创建 a ; a 指向 AAAFFF000 { n: 1 }
* => 创建 b ; b 指向 AAAFFF000 { n: 1 }
* => a.x 成员访问,优先级 > a
* => 创建 AAAFFF111 堆; a.x 指向 AAAFFF111 { n: 2 } ; AAAFFF000 { n: 1, x: AAAFFF111 }
* => a 指向 AAAFFF111 { n: 2 }
*
* ... ...
* 关闭页面,EC(G) => 出栈释放
*/
var a = { n: 1 };
var b = a;
a.x = a = { n: 2 };
console.log(a.x); //=> undefined
console.log(b); //=> { n: 1, x: { n: 2 } }
/*
* ECStack (此处不讨论变量提升)
* EC(G) => 进栈执行
* VO(G)
* => 创建堆 AAAFFF000; 创建 a; a 指向 AAAFFF000 [10, 23]
* => 创建堆 AAAFFF111; 创建 fn; fn 指向函数 AAAFFF111 (函数堆)
* 1.把函数体中的代码当作代码字符串存储到堆中,"代码字符串"
* 2.函数也是对象,有自己的键值对: name fn, length: 1, prototype, __proto__, ...
* 3.创建函数的同时,就已经定义了函数的作用域,当前创建函数所在的上下文 [[scope]]: EC(G)
* => 函数执行,把全局下 x 的值作为是残传递给函数的形参
* 1.执行函数的目的: 让存储在堆中的代码字符串执行
* 2.代码执行会形成自己的执行环境
* => EC(FN) fn(x) => 进栈执行 AAAFFF111(AAAFFF000)
* 1.把全局的上下文压缩到栈的底部
* 2.新进来的上下文放到栈的顶部
* AO(FN)
* => 代码执行之前
* (函数中的变量对象)活动对象,是 VO 的一个分支,都是变量对象
* 1.初始化作用域链 [[scopeChain]]: <EC()FN, EC(G)> (<执行时自己所在上下文, 函数的作用域>)
* * 私有上下文中执行代码时遇到变量按照链式查找来操作
* 2.初始化 this 指向: window
* 3.初始化实参集合 ARGUMENTS
* 4.形参赋值: y = AAAFFF000 (形参变量也存放到自己上下文中的私有变量对象中,是私有变量)
* 5.变量提升: --
* => 代码开始执行: y 是私有变量
* => y[0] = 100; // AAAFFF000: [100, 23]
* => y = [100]; // 创建堆 AAAFFF222; y 指向 AAAFFF222 [100]
* => y[1] = 200; // AAAFFF222: [100, 200]
* console.log(y); // AAAFFF222 [100, 200]
* =>EC(FN) 上下文中代码执行结束,没有东西被外部占用,出栈释放
* => console.log(x); // AAAFFF000 [100, 23]
*
* ... ...
* 关闭页面,EC(G) => 出栈释放
*/
var x = [12, 23];
function fn(y){
y[0] = 100;
y = [100];
y[1] =200;
console.log(y);
}
fn(x);
console.log(x);