JS 数据类型和堆栈

2,016 阅读3分钟

基本数据类型和复杂数据类型的本质区别

数据类型

JavaScript中的数据类型可以分为两大类:

  • 原始值 (基本类型)
    • Boolean
    • Null
    • String
    • Number
    • Bigint
    • Symbol
    • Undefined
  • 对象 (复杂类型)
    • Object
    • Array
    • Function

小结:数据分为两大类,对象和非对象

数据的存放

数据存放在内存里面,内存分:栈区(stack),堆区(heap)。

栈区

栈区的特点是:数据按顺序存放,内存大小由系统分配。

基本类型的存放示意图:

如果变量存储的是原始值,那么这个变量就是值类型,在 JS 里也叫做基本类型

堆内存

堆内存的特点是:数据随机存放,大小由程序员分配。

复杂类型的存放示意图:

如果变量存储的是内存位置,那么这个变量就是引用类型,在 JS 里也叫复杂类型,也就是对象。

行为差异

赋值

基本类型赋值

var a = 1
var b = a
    b = 0
console.log(a)  // 1

在栈内存中的数据发生复制行为时,系统会自动为新的变量分配一个新值。var b = a执行之后,a与b虽然值都等于1,但是他们其实已经是相互独立互不影响的值了,所以我们修改了b的值以后,a的值并不会发生变化。

复杂数据类型

var o1 = {name:"o1"}
var o2 = o1
o2.name = "o2"
console.log(o1.name)// o2 

当修改其中一个的值的时候,会影响到另一个。这是因为,这两个变量指向同一块内存,内存里的数据发生了变化,所以会发生变化。

求值策略

所谓求值策略也就是函数的传参方式,函数的参数可能是基本类型也可能是复杂类型,这两种类型在传参的时候有所区别。

按值传递

按值传递,参数的值是调用者传递的值的拷贝(copy of value),函数内部改变参数的值不会影响到外面的值,一般来说,是重新分配了新内存,该新内存块的值是外部值的拷贝,并且它的值是用到函数内部的。

var a = 1
function changeA (a) {
    a = 2
}
changeA()
console.log(a)// 1

这说明了,函数内部的a和函数外部的a是相互独立的,传参是将值拷贝一份。

那么对于复杂类型的传参呢。网上有各种各样的教程和文章,有的说按引用传递,什么共享传递,等等,其实很简单的道理,明白了上面的内存的分配,就很清楚的知道是怎么回事了,至于是叫按共享传递(Call by sharing)还是什么其他的都不重要。

还是按值传递

对于基本类型已经搞清楚了,传参的方式是按值传递,现在看复杂类型的传参:

这里分明是已经被改变了,不是说按值传递的,只是将值拷贝一份吗?再看一个例子:

这里没有被改变。。是不是有点迷糊。总结一下就是:修改参数的属性将会影响到外部,而重新赋值将不会影响到外部对象。 到底是怎么回事呢?实际上,还是按值传递,只不过这个值,不是指对象数据本身,而是这个对象的内存地址,也就是说,函数的传参是外部对象的地址的副本。这样就可以很清楚的解释了为什么二者会出现不同的结果。

总结:在函数传参的时候,不管是基本类型还是复杂类型,都是按值传递,对于基本类型是拷贝值也就是数据本身,而对于复杂类型,拷贝的是复杂类型在堆内存中的地址。