JavaScript学习系列之内存模型篇

一个热爱技术的菜鸟...用点滴的积累铸就明日的达人

正文

  如果真的想学好一门语言,那么一定要了解它内存模型,本篇文章就带你走进JavaScript的内存模型,由于本人才疏学浅,若有什么表述有误的地方,欢迎各位看官能够指点一二,在此不胜感激...

  在阅读这边文章之前,默认您已经掌握了JavaScript的基本概念、栈堆等基本数据结构以及计算机基本理论基础,如有了解欠缺,请移步相关博客后再阅读本文。

一、基本的数据类型的内存结构

  首先粗略的介绍一下JavaScript中五种基本的数据类型Undefined、Null、Boolean、Number、String;其中对于Undefined与Null的区别,网上有很多大牛都有介绍,在此本文暂不涉及,如有疑虑之处,请移步相关博客。或许有看官会问为什么在介绍JavaScript内存模型之前要先介绍JavaScript的基本数据类型的内存结构呢?这是因为JavaScript内存模型与基本数据类型的内存结构的关系就好比数学与实数的关系,基本数据类型的内存结构是整个JavaScript内存模型的基础。那么接下来就让我以最简短的方式来阐述一下基本数据类型的内存结构吧~

  基本数据类型的内存结构:在JavaScript中基本的数据类型都是以值的形式保存在内存中的。举个例子:

var inta = 10;
var strb = ‘Hello‘;

  那么在执行完这段JavaScript代码之后,内存中会有两个区域分别表示为inta,strb;其中表示inta区域的值为‘10’,表示strb区域的值为‘Hello’,也即表示inta与strb的内存区域保存的均为实际的真值;

二、引用数据类型的内存结构

  在JavaScript中除了基本数据类型,那就剩下引用数据类型了,所以在介绍玩基本数据类型内存结构之后,就很有必要再介绍一下引用数据类型内存结构。引用数据类型的真实对象是保存在堆内存中的,而JavaScript与Java相似,均不可以直接访问堆内存,所以都是使用“引用”这个东西来访问处于堆中的对象,引用与对象的关系可以描述成遥控器与电视机之间的关系,我们可以持有遥控器来操控电视机。所谓的引用其实就是一块内存的地址,即在表示引用的区域上保存的是内存中对象的内存地址值,如图所示:

  其中假设对象处于内存中一个位置叫做0x23215的区域,那么椭圆的区域表示这个对象的引用,椭圆区域中存的就是0x23125这个值,在实际的操作中执行环境会通过引用中存的0x23125,去找到内存中的这个对象。

三、内存模型

  在JavaScript执行时期,可以将内存从逻辑上划分为两部分:栈与堆。其中栈是在JavaScript执行时,用于储存执行上下文(后续文章会介绍)的,而堆是存储对象的区域。在执行上下文生成之后,会创建一个变量对象(后续文章会介绍),变量对象是一个特殊的对象,它也会存储在堆。基本数据类型往往都会直接保存在变量对象中,而引用数据类型实际上是在变量对象中保存一个引用指向对象的地址(也即引用本身)。

  学习完JavaScript中的内存模型之后,请各位看官看看下面这段代码,并且猜猜它的输出结果,以验证上述知识的理解程度:

var inta = 10;
var stra = ‘Hello‘;

var obja = {a: 10, b: ‘Hello‘};

var intb = inta;
var strb = stra;
var objb = obja;

intb = 20;
strb = ‘World‘;
objb.a = 20;
objb.b = ‘World‘;

console.log(inta, intb);
console.log(stra, strb);
console.log(obja, objb);

  运行结果:

10 20
Hello World
{ a: 20, b: ‘World‘ } { a: 20, b: ‘World‘ }

  这其中会涉及到对象的赋值问题,在对基本数据类型赋值的时候,都是将原值赋值到新的对象上,所以改变新的对象的值,并不会影响到原值(因为它们本质上保存的是两个值);而对引用数据类型赋值则是将引用所指向对象的地址赋值给另一个引用,而在后续操作中,如果通过新的引用去改变对象中内部的值的话,还是会影响原来的引用所指向的对象(因为它们本质上保存的是同一个对象)

四、内存回收机制

  JavaScript具有自动的垃圾回收机制,也即执行环境会负责代码的执行过程中使用的内存,它会定期(周期性)找出哪些不再使用的对象,然后释放其内存。目前JavaScript最常用的垃圾回收算法为标记清除算法,垃圾回收机制会通过标记的算法来决定哪些对象是不需要再次使用的,然后再进行清除,也即清理哪些被定义为垃圾的JavaScript对象。

  上述所述的不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量。全局变量的生命周期直到浏览器卸载页面才会结束,所以声明一个全局变量的时候,我们一定要慎重的考虑,在使用完这个变量的对象之后,我们是否还在需要这个对象,如果不需要的话,我们应该手动的将这个变量置为空,这样在下一次垃圾回收的时候,就能去释放这个变量上一次指向的对象(请注意变量与对象的区别)。

  下面请各位看官see一下以下的代码,来分析一下垃圾回收。

function fun1() {
    var obj = {name: ‘csa‘, age: 24};
}

function fun2() {
    var obj = {name: ‘coder‘, age: 2}
    return obj;
}

var f1 = fun1();
var f2 = fun2();

  在上述代码中,当执行var f1 = fun1();的时候,执行环境会创建一个{name:‘csa‘, age:24}这个对象,当执行var f2 = fun2();的时候,执行环境会创建一个{name:‘coder‘, age=2}这个对象,然后在下一次垃圾回收来临的时候,会释放{name:‘csa‘, age:24}这个对象的内存,但并不会释放{name:‘coder‘, age:2}这个对象的内存。这就是因为在fun2()函数中将{name:‘coder, age:2‘}这个对象返回,并且将其引用赋值给了f2变量,又由于f2这个对象属于全局变量,所以在页面没有卸载的情况下,f2所指向的对象{name:‘coder‘, age:2}是不会被回收的。

  由于JavaScript语言的特殊性(闭包...),导致如何判断一个对象是否会被回收的问题上变的异常艰难,这不仅需要我们有很强的基本功,还需要在以后的项目中积累经验。最后顺便说一句,即使随着硬件的更新换代以及垃圾回收机制的改进,我们也不应该忽视垃圾回收的基本理论,因为这是提高代码性能的关键一步,只有这样我们才能写出那些堪称艺术品的代码...

时间: 2024-10-19 23:28:21

JavaScript学习系列之内存模型篇的相关文章

JavaScript学习系列之执行上下文与变量对象篇

一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变量对象的基本理论知识,是理解闭包,原型链的关键所在(闭包与原型链会在接下来的文章中介绍).本篇文章就带你走进JavaScript的执行上下文与变量对象,由于本人才疏学浅,若有什么表述有误的地方,欢迎各位看官能够指点一二,在此不胜感激... 在阅读这边文章之前,默认您已经掌握了JavaScript的基

深入理解Java虚拟机- 学习笔记 - Java内存模型与线程

除了在硬件上增加告诉缓存之外,为了使得处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行(Out-Of-Order Execution)优化,处理器会在计算之后将乱序执行的结果重组,保证该结果与顺序执行的结果一致,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致,因此,如果存在一个计算任务依赖另外一个计算任务的中间结果,那么其顺序性并不能靠代码的先后顺序来保证.与处理器的乱序优化执行类似,Java虚拟机的即时编译器中也有类似的指令重排序(Instruction

JVM学习记录-Java内存模型(二)

对于volatile型变量的特殊规则 关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制. 在处理多线程数据竞争问题时,不仅仅是可以使用synchronized关键字来实现,使用volatile也可以实现. Java内存模型对volatitle专门定义了一些特殊的访问规则,当一个变量被定义为volatile时,它将具备以下两个特性: 第一个是保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的.而普通变量不能做到这

javascript基础学习系列-DOM盒子模型常用属性

最近在学习DOM盒子模型,各种属性看着眼花缭乱,下面根据三个系列来分别介绍一下: client系列 clientWidth :width+(padding-left)+(padding-right)->和内容溢出无关系 clientHeight:height+(padding-top)+(padding-bottom)->和内容溢出无关系 clientLeft:左边框的宽度 clientTop:上边框的高度(border[Left/Top]Width) offset系列 offsetParen

企业管理系统前后端分离架构设计 系列一 权限模型篇

前段时间分别用vue和react写了两个后台管理系统的模板vue-quasar-admin和3YAdmin.两个项目中都实现了基于RBAC的权限控制.因为本职工作是后端开发,比较清楚权限控制一个管理系统应该必须具备的核心功能,而且是可以做到通用的.打算写写关于管理系统前后端分离方面的文章,也是做一个知识的总结,其中会涉及到vue,react,node,.net core等方面的知识. 术语描述 用户(Subject):发起操作的主体 对象(Object):指操作所针对的客体对象,比如文章或评论

VS2015 VNext学习系列之三:CLR篇

1.概述 ASP.NET为了优化和云平台,甚至跨平台,DotNetFramework版本进行了重新设计,划分了多个版本,在应用程序中它们可以共存,你也可以单一使用某一个版本. 1.1完整的.NET CLR 它是在 VS创建项目的默认运行时.提供了整个 API 集,保持了向后兼容性. 1.2核心 CLR/云优化 它 是 ASP.NET 5 项目精简和完全模块化的运行时.通过重新分解为单独的组件运行时,可以提供改进的组件更多也很快为每个组件更新. 它大约 11 兆字节而不是完整的.NET CLR 大

?VS2015 VNext学习系列之四:Bower篇

1.概述 Bower:一个"web包管理",允许您安装和还原客户端软件包,它包含 JavaScript 和 CSS 库.(注:对于DLL安装包,仍将使用 NuGet 程序包管理器.) 2.项目 在前面例子项目“VNextDemo”解决方案中,右键:添加->新建项目: 选择模板: 注:创建完成后,把WebAppBowerGrunt项目“设为启动项目”! 3.目录结构

VS2015 VNext学习系列之五:Grunt篇

1.概述 Grunt:基于 JS 的task runners.它是一个应用程序,能自动化地开发一些任务. 2.添加less 在“WebAppBowerGrunt”项目根目录下,新建"Lesses"文件夹,在其内,添加site.less文件,其代码为: @bgcolor: red; body { background-color: @bgcolor; } 3.修改package.json 添加:"grunt-contrib-less": "^0.12.0&q

JavaScript学习系列7 -- JavaScript中的运算符

今天,我们来说一说JavaScript中的运算符,首先我们来讲一讲 一元运算符 JavaScript中的一元运算符有以下几种 1. delete delete 运算符主要用于删除对以前定义的对象属性或者方法的引用, 举个例子如下 var myObj = new Object(); // 这一句和 var myobj = new Object(); 等价 myObj.name = "Luke"; alert(myObj.name); // 输出 "Luke" dele