blog icon indicating copy to clipboard operation
blog copied to clipboard

基础笔记 - Javascript 变量存储

Open HXWfromDJTU opened this issue 4 years ago • 0 comments

Javascript 变量存储

最近在webpack调试的时候遇到,经常遇到heap oout of menory的情况,导致调试开发工作的十分不顺利。所以决心了解一下Javascriptnode的内存机制。

数据类型与存储空间

本篇文章先了解Javascript In Browser的内两种原始数据类型

类型 包含种类 存储情况
原始值 Number boolean null undefined 原始值存储在
引用值 Object Function Array 引用类型的指针存储在中,指向存在中的实际对象

堆与栈

栈(stack) 描述
结构 栈的优势就是存取速度比堆要快,仅次于直接位于CPU中的寄存器
值的变动 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本
大小与存活时间 所占内存空间大小、生命周期都是固定的
堆(heap) 描述
结构 因为在运行时动态分配内存,所以存取速度比较慢
值的变动 原地操作
大小与存活时间 可以动态地分配内存大小,生存期也不必事先告诉编译器

String 存储的特殊情况

存储空间

Javascript 中的 String 虽然是 原始类型,但事实上 V8 堆内使用(类似数组的)连续空间存储字符串。并且使用OneByteTwoByte两类结构来做存储。

字符串的不可变性(举例)

const str = 'abcdef'

// 可读
conso.log(str[2]) // c 

str[2] = 'x'

// 但不可写
console.log(str) // abcdef

Javascript的字符串属于基础类型,每次往字符串中加添加内容,其实在内存中都是新创建了一个新字符串对象,旧字符串对象短时间也还在内存中,但马上会被GC回收。

js函数传参是传值还是传引用?

首先我们给一个定论,是传值。而且所有情况都只传值。

在函数传参的时候,内核是复制了一个栈帧的值作为形参的值,栈帧上存的是什么,那么我们就复制的是什么。

基本类型

在基本类型传参的时候,我们同样理解为拷贝栈帧,此时栈帧中保存的就是基本类型的值,比如说123true,那么形参中的栈帧保存的也是一个这样的基本类型值。

引用类型

在传递引用类型的的时候。首先,实参在栈中存储的值就是一个指向堆中对应数据对象的指针,在传递函数参数的时候,将这个指针的值赋值了一份,作为形参的值。那么此时,形参的栈空间也存着一个值相同的指针,都指向了堆中的同一个对象。

举例说明

// 原对象
let a = {key:123};

// 转换方式1
function changeAttr(b){
  b.key = 789;
}

// 转换方式2
function changeItSelf(c){
  c = {key2:888}
}

// 开始试验
changeAttr(a);     
console.log(a);   // {key:789}

changeItSelf(a);  
console.log(a)    // {key:789}

我们可以看出changeAttr操作中,访问到了a中的内容,并且进行了修改。而changeItSelf中的内容并没有修改成功。来画一下当时的栈帧情况。

changeAttr

栈内存 -- 堆内存
a xxx0081270xx -- xxx0081270xx {key:123}

然后执行了函数changeAttr,进行了栈帧的拷贝,除了变量名字不同,值是完全的一致,所指向堆的位置也一致。也就不难明白为什么会修改到同一个对象了,以下是修改后的内存情况。

栈内存 -- 堆内存
a xxx0081270xx -- xxx0081270xx {key:123}
b xxx0081270xx

changeItSelf

而当调用changeItSelf的时候,这时候是对c的值直接进行修改,也就是赋值了a的值(也就是那个内存地址)之后,我们又用一个新的内存地址覆盖了c的值。

栈内存 -- 堆内存
a xxx0081270xx -- xxx0081270xx {key:789}
c a762784368wqe -- a762784368wqe {key:888}

所以到这里,我们坚定地继续喊出:JS函数调用的时候,形参的到的就是实参的值

指针(pointer)与引用(reference)

这个问题其实比较绕的原因是,大多数的前端工程师不知道指针的定义

在计算机科学中,指针(英语:Pointer),是编程语言中的一类数据类型及其对象或变量,用来表示或存储一个存储器地址,这个地址的值直接指向(points to)存在该地址的对象的值。 --- 《维基百科 - 指针》

JavaScript中没有指针这个数据类型,也不能够将一个变量作为另一个变量的指针

JavaScript中没有指针,引用的工作机制也不尽相同。在JavaScript中变量不可能成为指向另一个变量的引用。 ———《你不知道的JavaScript 中卷》2.5 值和引用,第1版28页。

但是,无论语言怎么变,都脱离不了冯诺依曼模型,CPU内存

  • 我们可以认为Javascript中没有指针,因为没有指针这种数据类型,也不能将一个变量指向另一个变量。

  • 我们也可以认为JavaScript中的引用和指针是类似的一套体系,都是实际内容存储的内存空间的一个地址。

  • JavaScript变量赋值、传递的时候,时钟传递的是,这个本身是一个基础类型的内容,异或是对象的引用就交给函数解析器去理解了。

其他的声音

Q: JavaScript字符串的内容存储在哪里,是堆中还是栈中?

A: 你写JS里面用到的所有东西,字符串,数字,数组,对象,甚至整个代码片段(文件),在JS引擎里面都会 new 一个指针(对象引用)出来,所以如果你要问字符串在堆还是栈,那么可以说是堆。然而脚本语言引擎的目的就是为了托管内存管理,让你不用去知道到底是堆还是栈。

----- from KennethJ - 知乎 传送门

参考资料

[1] 图1 - 链接
[2] 字符串在 V8 内的表达 - @superzheng
[3] [讨论] JavaScript中String的存储 - iteye

HXWfromDJTU avatar Jul 18 '20 10:07 HXWfromDJTU