The basic data types of memory size is fixed (static memory allocation), and reference data types of dynamic memory size is not fixed (dynamic memory allocation), may change at any time. So there will be some differences in the memory allocation stage two data types.
The difference between static and dynamic memory allocation memory allocation in the following table:
Static memory allocation | Dynamic memory allocation |
Compilation phase to determine the size | Unable to determine the size of the compile phase |
At compile time | Executed at runtime |
Assigned to the stack | Assigned to the heap |
Assigned sequentially, last in, first out (LIFO) | Disorderly distribution |
Memory Allocation in JavaScript
Using basic data types and the reference data type are graphs showing memory allocation process, to be understood that the underlying details of JavaScript.
First, we start with a simple basic data types assignment, as follows:
let num = 1;
When the JavaScript engine to perform this line of code, it performs the following operations:
1 creates a variable unique identifier (identifier) --- num, and the identifier for the stack memory address A1
formation mappings.
2 assign it an address on the stack memory A1
.
The value 3 1
is stored in the assigned address.
Example is as follows:
Usually we say that num
the value of the variable is equal 1
, but in fact, strictly speaking, num
the value of the variable is equal to the stack memory stored in the memory address corresponding to the value (as shown in A1
).
Next we create a new variable newNum
and num
assign to it:
let newNum = num;
After the above assignment, often said newNum
value is equal to the value num 1
, likewise from the strict sense of the word refers to newNum
and num
point to the same memory address A1
, as shown below:
If then we do the following to see what happens:
num = num + 1 ;
We num
self-growth variables, it is clear that num
the value of the variable 2
. Because newNum
and num
point to the same memory address A1
, then the time newNum
value of whether to 2
do, before answering this question, we first look at the current memory address changes occur:
In the figure above we can see that num
the memory address of the variable has changed from the original A1
becomes A2
, because in the JS in the basic data types are immutable , once modified, will assign new memory address the new value is stored and modified to the new address, so to answer that question above, newNum
the value remains the same, still is 1
, because its memory address has not changed.
Look at the following example:
let str = 'ab'; str = str + " c " ;
Because the string also belongs to the basic data types, the basic data types are immutable, even if the above code simply be c
spliced into the original string ab
back, but still it is assigned a new memory address, the variable str
will eventually point this new memory address, as shown below:
Understanding of the basic data types of memory allocation, then we have to understand the type of data memory allocation under reference. Similarly, we quote from a simple data type assignment start:
let arr = [];
When the JavaScript engine to perform this line of code, it performs the following operations:
1 为变量创建一个唯一标识符(identifier)---arr,该标识符用于与栈内存中的地址A3
形成映射关系。
2 在栈内存中为其分配一个地址A3
。
3 在栈内存中存储堆中分配的内存地址的值H1
。
4 在堆中存储分配的值空数组[]
。
示例图如下:
在JavaScript引擎(例如Chrome和Node的V8引擎)中主要是由两个部件组成,一个叫内存堆(Memory Heap),一个叫调用堆栈(Call Stack)。其中调用堆栈除了函数调用之外,主要用于存放基本数据类型的值,而引用数据类型的值一般都存放在内存堆中,堆中存放的数据都是无序的并且可以动态地增长,所以非常适合用于存储数组和对象。
let和const的对比
在了解完以上两种数据类型的内存分配方式后,我们这里对let
和const
的使用方式进行一下对比,通常来说,我们建议在写代码的过程中能使用const
的地方尽量减少使用let
,这样可以在某种程度上避免变量被无端修改而引发的一系列问题。如下代码:
let num = 1; num = num + 1; let arr = []; arr.push(1); arr.push(2); arr.push(3);
在上述代码中,变量num
因为使用let
的方式声明,所以允许其被修改,因为基本类型的值是不可变的,所以会为num
变量分配新的内存地址。对于arr
变量,这里同样使用let
方式进行声明,表示允许其修改,但是对于push
操作其实并没有修改arr
变量的内存地址,只是将新的值推入了堆内存的数组中,所以此处建议修改为使用const
进行声明。
如下示例:
const num = 1; num = num + 1;
由在上边了解到的基本数据类型的内存分配方式,我们知道为变量num
在栈内存中分配了一个地址来保存对应的值。
但是这里我们是使用const
的方式来进行声明的,当我们重新为变量num
进行赋值时,JS尝试为其分配新的内存地址,那么这里也就是抛出错误的地方,因为我们明确不允许对其进行修改。
因此在控制台中我们会看到对应的报错信息。
再看如下示例:
const arr = [];
对于引用数据类型,我们知道会在栈内存上为其分配内存地址,存储的是堆中的内存地址的值。
我们做如下操作:
arr.push(1); arr.push(2); arr.push(3);
执行push
操作实际上是将新值推入堆中的数组,内存地址并没有发生改变。这也就是为什么虽然使用const
声明变量,但是依旧没有报错的原因。但是如果我们使用如下方式:
arr = 1; arr = undefined; arr = null; arr = []; arr = {};
这些方式都会修改原数组的内存地址,const
声明是不允许修改内存地址的,所以很明显会抛出错误。因此这里也是建议默认情况下使用const
声明变量,除非需要修改内存地址,const
声明的变量必须在声明时进行初始化,也方便了其他前端人员能一眼看出哪些变量是不可变的。