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

正文
  如果真的想学好一门语言,那么一定要了解它内存模型,本篇文章就带你走进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语言的特殊性(闭包...),导致如何判断一个对象是否会被回收的问题上变的异常艰难,这不仅需要我们有很强的基本功,还需要在以后的项目中积累经验。最后顺便说一句,即使随着硬件的更新换代以及垃圾回收机制的改进,我们也不应该忽视垃圾回收的基本理论,因为这是提高代码性能的关键一步,只有这样我们才能写出那些堪称艺术品的代码...

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

  1. JVM学习笔记——内存模型篇

    JVM学习笔记--内存模型篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的内存模型部分 我们会分为以下几部分进行介绍: 内存模型 乐观锁与悲观锁 synchronized优化 内 ...

  2. JavaScript学习系列5 ---ES6中的var, let 和const

    我们都知道JavaScript中的var,在本系列的 JavaScript学习系列2一JavaScript中的变量作用域 中,我们详细阐述了var声明的变量的作用域 文章中提到,JavaScript中 ...

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

    一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变 ...

  4. 【JVM】JVM系列之内存模型(六)

    一.前言 经过前面的学习,我们终于进入了虚拟机最后一部分的学习,内存模型.理解内存模型对我们理解虚拟机.正确使用多线程编程提供很大帮助.下面开始正式学习. 二.Java并发基础 在并发编程中存在两个关 ...

  5. JVM学习总结一——内存模型

    JVM是java知识体系的基石之一,任何一个java程序的运行,都要借助于他.或许对于我这种初级程序员而言,工作中很少有必要刻意去关注JVM,然而如果能对这块知识有所了解,就能够更清晰的明白程序的运行 ...

  6. 浅谈JavaScript原型图与内存模型

    js原型详解 1.内存模型: 1.原型是js中非常特殊一个对象,当一个函数(Person)创建之后,会随之就产生一个原型对象 2. 当通过这个函数的构造函数创建了一个具体的对象(p1)之后,在这个具体 ...

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

    对于volatile型变量的特殊规则 关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制. 在处理多线程数据竞争问题时,不仅仅是可以使用synchronized关键字来实现,使用vo ...

  8. JVM学习记录-Java内存模型(一)

    前言 Java虚拟机规范中定义了一种Java的内存模型,即Java Memoory Model(简称JMM),用来实现让Java程序在各个平台下都能达到一致的内存访问效果.JVM是整个虚拟机,JMM模 ...

  9. JVM系列.JVM内存模型

    <Java虚拟机规范>将虚拟机的内存分为以下几个区域: 堆区:堆区是JVM中最大的一块内存区域,按照垃圾分代收集的角度划分,又可以分成年轻代和老年代,而年轻代内存又被分成三部分,Eden空 ...

随机推荐

  1. setTimeout模拟setInterval调用

    在开发环境下,尽量不用间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束之前启动. 尽量使用超时调用来模拟间歇调用. 示例代码: <!DOCTYPE html> <html l ...

  2. 〖Linux〗通过USB连接,共享Linux上的网络,使用Android手机上网。

    1. Android手机上打开USB共享 2. Linux上修改USB0的ip地址并启用 sudo ifconfig usb0 192.168.42.127 up 3. Android手机上修改路由表 ...

  3. 〖Android〗/system/etc/fallback_fonts.xml

    <?xml version="1.0" encoding="utf-8"?> <!-- Fallback Fonts This file sp ...

  4. Redhat 5禁止IPv6

    Redhat  5禁止IPv6 IPv6还没有全然普及,可是安装完系统之后IPv6是有效的,在一定程度上影响网络性能,所以在我们在全然不使用IPv6的情况下.最好关闭IPv6.如今我们就在本文以完整的 ...

  5. 无源码情况下直接修改jar里内容思路

    当我们反编译的jar包里class被混淆过,这时反编译出来的java文件会有各种奇葩的问题,不能直接用,比如某框架需要注册码,这个时候我们只能通过层层反编译将验证码相关的部分绕过,如果这个代码不是那么 ...

  6. cordova build android get Execution failed for task ':dexArmv7Debug'

    本篇文章由:http://xinpure.com/cordova-build-android-get-execution-failed-for-task-dexarmv7debug/ XGPush 腾 ...

  7. 树莓派/RaspberryPi 内核源码下载

    树莓派的源码有两种下载方式:压缩包下载和git clone指令下载. 1.压缩包下载 选择对应分支,点击Github界面的 下载按钮即可,如下图: 测试发现,同样的分支,用压缩包方式下载后编译会出错, ...

  8. 如何用php启动exe程序,并在进程中查看?

    function query_process($service) { /* **查看WINDOWS系统进程列表,并查找指定进程是否存在 */ $tasklist = $_SERVER["WI ...

  9. hdu 2066 一个人的旅行(dijkstra)

    一个人的旅行 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub ...

  10. modelsim仿真中 do文件的写法技巧

    网上的关于DO文件的编写好像资料不多,比较杂,所以本人总结一下常用的简单语法,方便大家查看.其实本人也刚接触DO文件没多久,有纰漏很正常,欢迎指正批评,互相学习.PS:写得有点乱   还有一个值得注意 ...