JavaScript堆栈内存的底层处理机制
JS代码可以运行的环境
- 浏览器「内核:webkit(blink)、gecko、presto、trident、Chromium…」。
- IOS/安卓原型App中,基于webview运行页面和JS「内核:webkit」。
- 特点:支持window、没有global、支持ES6Module模块规范、不支持CommonJS模块规范。
- Node「内核:webkit」。
- 特点:支持global、不支持window、支持CommonJS、不支持ES6Module。
- webpack「基于Node环境编译打包JS,最后把编译后的结果运行在浏览器中」。
- 特点:浏览器和Node环境支持的他都支持。
- 不论什么环境下执行代码,总要开辟出相关的内存(执行内存/内存条),用来存储值「Heap堆内存」以及运行代码「Satck栈内存 -> E(Execution)C(Context)Stack执行环境栈」。
浏览器执行JavaScript代码
JS之所以能够在浏览器中运行,是因为浏览器给JS提供了执行的环境 => 栈内存(Stack), 浏览器会在计算机内存中分配出一块内存,专门用来供代码执行的 =>栈内存 ECStack (Execution Context Stack) 执行环境栈。
- 从电脑内存中分配出一块内存(栈内存:Stack),用来执行代码。
- 分配一个主线程用来自上而下执行JS代码。
- 代码开始逐行进栈执行。
- 代码执行完比出栈,下一行代码才能进栈。
JavaScript中的堆栈内存
都是在计算机内存中开辟的空间
栈内存
- Stack :ECStack (Execution Context Stack)
- 说明:栈内存(执行环境栈)是浏览器在计算机中分配的一块内存,专门用来供代码执行的。
- 提供代码执行的环境。
- 存储基本类型值(原始值类型:变量、堆的引用地址)。
- 提供变量对象(VO、AO)存储当前上下文中声明的变量
堆内存
- 堆内存Heap
- 说明:浏览器会把内置的一些属性方法放到一个单独的内存中,堆内存(Heap)。
- 存储对象的值。
- 只要是对象,就会在Heap中开辟一块空间,每一个空间16进制的内存地址,用来存储对象的键值对,或者函数的代码字符串。
执行上下文 EC
- EC(Execution Context):
- 说明:执行上下文,代码执行所在的环境。
- 常见上下文分类:
- 全局上下文 EC(G)
- 函数私有上下文 EC(fn)
- 块级私有上下文 EC(block)
- 产生私有上下文 => 进栈执行 => 出栈(可能释放)
- 变量对象:当前上下文中,用来存储声明的变量的地方
- VO(Varibale Object):VO(G)或者VO(block)
- AO(Active Object):AO(?)
全局对象GO
- GO:Global Object
- 说明:GO是一个堆内存,(存储的都是浏览器内置的API属性方法)。
- 在浏览器端,window指向GO。
- 在全局上下文中
- 基于 let / const 声明的变量,是存储在VO(G)中的。
- 基于 var / function 声明的变量是直接存储在GO中的。
- 所以,严格意义来说,基于 var / function 声明的变量不能称之为全局变量,仅是全局对象中的一个属性而已。
全局的执行上下文 EC(G)
- EC(G) : Execution Context(global)
- 说明:形成的全局执行上下文进入到栈中执行进栈,执行完代码,可能会把形成的上下文出栈释放“出栈”,全局执行上下文页面关闭时才会释放。
- 函数中的代码都会在一个单独的私有的执行上下文中处理。
- 块级的执行上下文。
- 代码执行之前,首先形成自己的执行上下文,然后把上下文进栈,进栈后,在当前上下文中依次执行代码。
块级私有上下文EC(block)
- 除函数和对象外的大括号(判断题,循环体,代码块…)
- 如果在大括号中出现了let 、const、function、class等关键字声明变量,
- 则当前大括号会产生一个 ‘块级私有上下文’ ;
- 它的上级上下文是所处的环境,var 不产生,也不受上下文影响。
- 块级作用域的let 练习题:
- 块级作用域的function 练习题
JavaScript中的变量
变量对象 VO
- VO:Varibale Object
- 说明:在当前的上下文中,用来存放创建的变量的地方(每一个执行上下文中都会有一个自己的变量对象。
全局变量对象 VO(G)
- 全局上下文中用来存储全局变量的空间。
- 它不是GO ,只不过是某些情况下VO(G)中的东西会和GO中的东西有所关联而已,“映射机制”。
活动对象AO
- AO:Activation Object: 函数私有上下文叫做AO(activation Object)活动对象,但是也是变量对象。
创建一个变量对象(基本数据类型值)
var a =1;
- 创建一个值,把它存储到当前栈内存值存储空间当中(简单的基本类型值是这样存储的,复杂的引用类型值不是这样操作的)。
- 声明(declare)一个变量,放到当前栈内存变量存储区域中。
- 用 ‘=’ 赋值(定义:defined),其实赋值是让变量和值相互关联的过程。
创建一个变量对象(引用类型值)
var a = { n:1, m:2};
- 对象内存值按地址来操作:
- 对象:在内存中分配出一块新内存(堆内存 => heap),用来存储引用类型值 ,堆内存都有一个16进制地址。
- 函数:内存空间存储三部分信息
- 作用域 [[scope]] :当前所处的上下文。
- 函数体中的代码字符串。
- 当做普通对象存储的静态属性和方法[name & length]。
- 声明一个变量a,放到当前栈内存变量存储区域中。
- 把堆内存地址和变量关联起来。
全局上下文中访问某个变量。
- 首先看VO(G) 是否有,有就是全局变量。
- 没有再基于window看GO有没有,有则是全局对象的一个属性。
- 如果还是没有,则报错 xxx is not defined。
‘全局上下文’ 给变量赋值 a = 100
- 先看是否是全局变量,如果之前GO中有,就是修改属性值。
- 如果不是则直接给GO加一个这个属性。
练习题:
let a = {n:1};
let b = a;
a.x = a = {n:2};
console.log(a.x);
console.log(b);
答案及解析:
变量提升
变量提升机制
-
变量提升是JavaScript执行代码的预处理机制。
-
在当前上下文中(全局、私有、块级),JavaScript代码自上而下执行之前,浏览器会提前处理一些事情:
- 词法解析,词法解析一定发生在代码执行之前。
- 变量提升
- 会把带
VAR
和FUNCTION
关键字的进行提前声明或者定义。 - 带
VAR
关键字的变量提升阶段只声明(declare):var a
,不定义,默认值是undefined
。- 执行到代码行再进行变量定义(defined):
a = 10
。
- 执行到代码行再进行变量定义(defined):
- 带
FUNCTION
关键字的变量提升阶段声明加定义全部完成。
- 会把带
-
FUNCTION
与VAR
没有优先级区别,是自上而下执行的。 -
建议使用函数表达式方式创建函数,函数执行只能在创建函数的之后,定义之前使用函数就会报语法错误
TypeError:fn is not a function
,保证逻辑严谨。
变量提升的意义
- 变量提升是为了让我们在创建变量之前使用变量而不报错。
- 在全局上下文中,
- 基于
VAR
和FUNCTION
声明的变量(全局变量)会映射到GO(全局对象window)上一份作为它的属性; - 接下来是一个修改,另外一个也会修改。
- 基于
词法解析
- 最开始 浏览器从服务器端获取的都是JS的文本字符串,只不过声明了其格式是[
Content-Type:application/javascript;
],浏览器首先按照这个格式去解析代码,这个过程称之为“词法解析” 阶段, 目的是生成 ‘AST词法解析树’。 - 基于
let / const
声明的变量- 在词法解析阶段,其实已经明确了,未来在此上下文中,必然会出现这些变量;
- 代码执行过程中,如果在具体声明代码前使用这些变量,浏览器会抛出错误:
Uncaught ReferenceError: Cannot access 'xxx' before initialization
!!
条件判断中的变量提升
- 在当前行下文中,变量提升阶段,不论条件是否成立,都要进行变量提升
var
:新老版本没有区别, 还是只声明,不定义(赋值 defined)。function
:- 老版本:判断体中的函数,还是声明加定义。
- 新版本:判断体中的函数,在变量提升阶段只声明,不定义。
匿名函数具名化
- 把原本作为值的匿名函数表达式 “具名化”:
- 虽然起了名字,但是这个名字不能在函数外面访问,
- 也就是不会在当前上下文创建这个名字。
- 当函数执行时,
- 在形成的私有上下文中,会把这个具名化的名字作为私有上下文中的变量(值就是当前函数)来处理。
- 递归调用时替代严格模式下不支持的
arguments.callee
。
var fn = function A(){
console.log("OK")
console.log(A);//=>"functionA(){...}"
A();//递归调用时替代严格模式下不支持的arguments.callee
}
A();// A is not defined
fn();
变量提升练习题1
console.log(fn);
function fn(){console.log(1);};
console.log(fn);
var fn = 12;
console.log(fn);
function fn(){console.log(2);};
console.log(fn);
答案解析:
变量提升练习题2
console.log(a);
if(!(a in window)){
var a = 13;
function fn(){};
};
console.log(a);
// key in obj :验证属性是否是obj的属性,不论是私有的还是公有的。
答案解析: