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) 执行环境栈。

  1. 从电脑内存中分配出一块内存(栈内存:Stack),用来执行代码。
  2. 分配一个主线程用来自上而下执行JS代码。
  3. 代码开始逐行进栈执行。
  4. 代码执行完比出栈,下一行代码才能进栈。

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;

  1. 创建一个值,把它存储到当前栈内存值存储空间当中(简单的基本类型值是这样存储的,复杂的引用类型值不是这样操作的)。
  2. 声明(declare)一个变量,放到当前栈内存变量存储区域中。
  3. 用 ‘=’ 赋值(定义:defined),其实赋值是让变量和值相互关联的过程。

创建一个变量对象(引用类型值)

var a = { n:1, m:2};

  1. 对象内存值按地址来操作:
    1. 对象:在内存中分配出一块新内存(堆内存 => heap),用来存储引用类型值 ,堆内存都有一个16进制地址。
    2. 函数:内存空间存储三部分信息
      1. 作用域 [[scope]] :当前所处的上下文。
      2. 函数体中的代码字符串。
      3. 当做普通对象存储的静态属性和方法[name & length]。
  2. 声明一个变量a,放到当前栈内存变量存储区域中。
  3. 把堆内存地址和变量关联起来。

全局上下文中访问某个变量

  • 首先看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代码自上而下执行之前,浏览器会提前处理一些事情:

    • 词法解析,词法解析一定发生在代码执行之前。
    • 变量提升
      • 会把带 VARFUNCTION 关键字的进行提前声明或者定义。
      • VAR 关键字的变量提升阶段只声明(declare):var a ,不定义,默认值是undefined
        • 执行到代码行再进行变量定义(defined):a = 10
      • FUNCTION 关键字的变量提升阶段声明加定义全部完成。
  • FUNCTIONVAR 没有优先级区别,是自上而下执行的。

  • 建议使用函数表达式方式创建函数,函数执行只能在创建函数的之后,定义之前使用函数就会报语法错误 TypeError:fn is not a function,保证逻辑严谨。

变量提升的意义

  • 变量提升是为了让我们在创建变量之前使用变量而不报错。
  • 在全局上下文中,
    • 基于VARFUNCTION 声明的变量(全局变量)会映射到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的属性,不论是私有的还是公有的。

答案解析:在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值