堆和栈都是运行时内存中分配的一个数据区,因此也被称为堆区和栈区,但二者存储的数据类型和处理速度不同。堆(heap)用于复杂数据类型(引用类型)分配空间,例如数组对象、object对象;它是运行时动态分配内存的,因此存取速度较慢。栈(stack)中主要存放一些基本类型的变量和对象的引用,其优势是存取速度比堆要快,并且栈内的数据可以共享,但缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

1. 栈的使用规则

栈有一个很重要的特殊性,就是存在栈中的数据可以共享。例如下面的代码定义两个变量,变量的值都是数字类型。

var a=3;
var b=3;

JavaScript结石引起先处理 var a=3;,首先会在栈中创建一个变量为a引用,然后查找栈中是否有3这个值,如果没有找到,就将3存放进来,然后将a指向3。接着处理 var b=3;,在创建为b的引用变量后,查找栈中是否有3这个值,因为此时栈中已经存在了3,便将b直接指向3。这样,就出现了a与b同时指向3的情况。此时,如果再令a=4,那么JavaScript解释引擎会重新搜查栈中是否有4这个值,如果有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

2. 堆的使用规则

下面通过Array来看一下堆的行为,例如存在下面的代码:

var fruit_1="apple";
var fruit_2="orange";
var fruit_3="banana";
var oArray=[fruit_1,fruit_2,fruit_3];
var newArray=oArray;

当创建数组时,就会在堆内存创建一个数组对象,并且在栈内存中创建一个对数组的引用。变量fruit_1、fruit_2、fruit_3为基本数据类型,它们的值直接存放在栈中;newArray、oArray为复合数据类型(引用类型),他们的引用变量存放在栈中,指向于存放在堆中的实际对象。

注意,newArray的值等于变量引用oArray,所以它也是复合数据类型(引用类型)。此时,如果更改变量fruit_1、fruit_2、fruit_3的值,那么其实是更改栈中的值;如果更改newArray或oArray的值,那么其实是更该堆中的实际对象,因此,对两个变量引用都会发生作用。例如,首先更改newArray的值,然后看oArray的值,代码如下:

alert(oArray[1]);// 返回 orange
newArray[1]="berry";
alert(oArray[1]);// 返回 berry

同样,首现更改oArray的值,然后看newArray的值,代码如下:

alert(newArray[1]);// 返回 orange
oArray[1]='tomato';
alert(newArray[1]);// 返回 tomato

JavaScript堆不需要程序代码来显示地释放,因为堆是由自动的垃圾回收来负责的,每种浏览器中的JavaScript解释引擎有不同的自动回收方式,但一个最基本的原则是:如果栈中不存在对堆中某个对象的引用,那么就认为该对象已经不再需要,在垃圾回收时就会清除该对象占用的内存空间。因此,在不需要时应该将对对象的引用释放掉,以利于垃圾回收,这样就可以提高程序的性能。释放对对象的引用最常用的方法就是为其赋值为null,例如下面代码将newArray赋值为null:

newArray=null;

3. 易犯的错误

在堆和栈的使用问题上,最易犯的错误就是String的使用,例如下面的代码:

var str=new String('abc');
var str='abc';

同样是创建两个字符串,第一种是用new关键字来新建String对象,对象会存放在堆中,每调用一次就会创建一个新的对象;而第二种是在栈中,栈中存放值‘abc’和对值的引用。推荐使用第二种方式创建多个'abc'字符串,这种写法在内存中只存在一个值,有利于节省内存空间。同时它可以在一定程度上提高程序的运行速度,因为存储在栈中,其值可以共享,并且由于栈访问更快,所以对于性能的提高大有裨益。而第一种方式每次都在堆中创建一个新的String对象,而不管其字符串值是否相等及是否有必要创建新对象,从而加重了程序的负担。并且堆的访问速度慢,对程序性能的影响也大。另外,出于逻辑运算的考虑,当对两个变量进行比较时,使用堆和栈存储就会有差异。下面来看一下逻辑等于和逻辑全等运算,深入理解一下堆和栈:

(1) 例如下面的代码,实际只比较栈中的值:

var str1='abc';
var str2='abc';
alert(str1==str2); // true
alert(str1===str2); // true

不管是逻辑等于和逻辑全等运算都返回true,可以看出str1和str2指向同一个值。

(2)例如下面的代码,实际只比较堆中的值:

var str1=new String('abc');
var str2=new String('abc');
alert(str1==str2); // false
alert(str1===str2); // false

不管是逻辑等于还是逻辑全等都返回false,可以看出str1和str2指向的不是同一个对象。

(3)例如下面的代码,比较堆和栈中的值:

var str1=new String('abc');
var str2='abc';
alert(str1==str2); // true
alert(str1===str2); // false

在进行逻辑等于和逻辑全等运算时,会首先将变量转成相同的数据类型,然后进行对比。变量str1和str2的数据类型虽然不同,但比较运算还是返回true。但逻辑全等运算与逻辑等于运算不同,它会对数据类型进行比较,看是否是引用的同一个数据。

JavaScript中内存使用规则--堆和栈的更多相关文章

  1. JVM内存结构之堆、栈、方法区以及直接内存、堆和栈区别

    JVM内存结构之堆.栈.方法区以及直接内存.堆和栈区别 一.  理解JVM中堆与栈以及方法区 堆(heap):FIFO(队列优先,先进先出):二级缓存:*JVM中只有一个堆区被所有线程所共享:对象和数 ...

  2. 前端知识体系:JavaScript基础-原型和原型链-理解原型设计模式以及 JavaScript中的原型规则

    理解原型设计模式以及 JavaScript中的原型规则(原文地址) 1.原型对象:我们创建的每一个函数(JavaScript中函数也是一个对象)都有一个原型属性 prototype,原型属性实质上是一 ...

  3. C语言中内存对齐规则讨论(struct)

    C语言中内存对齐规则讨论(struct) 对齐: 现代计算机中内存空间都是按着byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地 ...

  4. javascript内存管理(堆和栈)和javascript运行机制

    内存基本概念 内存的生命周期: 1.分配所需的内存 2.内存的读与写 3.不需要时将其释放 所有语言的内存生命周期都基本一致,不同的是最后一步在低级语言中很清晰,但是在像JavaScript 等高级语 ...

  5. iOS开发中的内存分配(堆和栈)

    进程的内存分区 所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等.不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先 ...

  6. [java学习笔记]java语言基础概述之内存的划分&堆和栈

    1.内存的划分 1.寄存器 cpu处理 2.本地方法区 和所在系统相关 3.方法区 方法加载进内存,其实就是开辟了一块该方法的方法区 方法区中还可以有静态区,用于存放静态变量(类变量) 4.栈内存 5 ...

  7. java内存管理(堆、栈、方法区)

    java内存管理 简介 首先我们要了解我们为什么要学习java虚拟机的内存管理,不是java的gc垃圾回收机制都帮我们释放了内存了吗?但是在写程序的过程中却也往往因为不懂内存管理而造成了一些不容易察觉 ...

  8. Java内存分配之堆、栈和常量池

    Java内存分配主要包括以下几个区域: 1. 寄存器:我们在程序中无法控制 2. 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 3. 堆:存放用new产生的数据 4. 静 ...

  9. C++内存分区:堆、栈、自由存储区、全局/静态存储区和常量存储区

    日志                                                                                                  ...

随机推荐

  1. ~psd面试 求最长回文序列 DP求解

    链接:https://www.nowcoder.com/acm/contest/90/D来源:牛客网 掌握未来命运的女神 psd 师兄在拿了朝田诗乃的 buff 后决定去实习. 埃森哲公司注册成立于爱 ...

  2. SpringBoot 整合 Swagger2

    1. Swagger UI 按以下步骤配置,项目启动后访问:http://localhost:8080/swagger-ui.html 1.1 添加依赖 <dependency> < ...

  3. 【Python】excel读写操作 xlrd & xlwt

    xlrd ■ xlrd xlrd模块用于读取excel文件内容 基本用法: workbook = xlrd.open_workbook('文件路径') workbook.sheet_names() # ...

  4. LeetCode算法一题型一以及解答。

    题目: 给定一个整数数列,找出其中和为特定值的那两个数. 你可以假设每个输入都只会有一种答案,同样的元素不能被重用. 分析: 普遍方法是直接遍历两遍数组,第一遍用target-nums[i],第二遍找 ...

  5. JavaScript之隐式类型转换

    布尔操作符(!.&&.||) 当使用 条件判断语句(if...else) 以及 布尔操作符(!.&&.||) 时,会调用Boolean()进行隐式类型转换 转换为fal ...

  6. C语言第二次博客作业---分支结构 陈张鑫

    一.PTA实验作业 题目1:计算分段函数[2] 本题目要求计算下列分段函数f(x)的值: 1.实验代码 int main(){double x,y; scanf("%lf",&am ...

  7. jsonp处理

    def loads_jsonp(self,_jsonp): try: return json.loads(re.match(".*?({.*}).*",_jsonp,re.S).g ...

  8. oracle删除某个用户所有表(转)

    1. select   'Drop   table   '||table_name||';'             from   all_tables           where   owner ...

  9. centos 安装配置 mysql

    安装环境:CentOS7 64位 MINI版,安装MySQL5.7 1.配置YUM源 在MySQL官网中下载YUM源rpm安装包:http://dev.mysql.com/downloads/repo ...

  10. 学大伟业 国庆Day2

    期望得分:30+100+0=130 实际得分:30+100+20=150 忍者钩爪 (ninja.pas/c/cpp) [问题描述] 小Q是一名酷爱钩爪的忍者,最喜欢飞檐走壁的感觉,有一天小Q发现一个 ...