js变量在内存的存放解惑
js变量的类型
我们知道js变量有两种类型
- 基本类型值,指简单的数据段,对于undefined、null、boolean、number、string这5种简单数据类型可以直接操作保存在变量中的实际值,也就是按值访问。
- 引用类型值,指那些可能由多个值构成的对象,只能操作对象的引用而不是实际的对象,所以要得到引用类型这种值只能按引用访问。
变量的存放方式
有如下代码段(赋值基本类型)
1 | var a=1; |
上述代码定义了两个变量,定义a=1在栈内存开辟一块空间存放1这个值,同理定义b=1也开辟一块内存存放1这个值;
当修改变量的值的时候,比如重新赋值a=3,那么就会重新开辟一块内存,将3存入后,把a指向3所在的内存位置;
有个问题还没考证,就是不同的变量定义的值一样的时候,是不是指向的是同一块内存。在python中可以通过id来访问存放位置的id,在值(基本类型)较小的时候,就是指向同一块内存
有如下代码段(赋值引用类型)
1 | var c={name:'阿Q'}; |
上述代码定义了两个变量,定义c的时候在栈内存开辟一块空间存放一个内存地址,这个内存地址指向堆内存中放{name:'阿Q'}这个对象的内存;d同样如此,而且两个对象是毫无关系的,他们的内存地址是不一样的。
当修改变量的值的时候,如c={name:'老王'},这种情况就和基本类型一样,属于重新赋值,就会将对象{name:'老王'}存到堆内存中,将对象的地址存到栈内存中,c指向这个内存;
当修改变量,如c.name='老张',这种情况可以看作给对象做一个修正或者扩展,不属于重新赋值,虽然结果是修改了对象,但并没有改动对象所在的位置,即c指向的栈内存里存放的对象的地址并没有变;
当然如果如下定义
1 | var c=d={name:'阿Q'}; |
这样的连等赋值,有另外的说法,之后专写一篇关于连等赋值的问题。
函数传参是按值传递还是引用传递
对于这块内容,网上很多博文说法很多,还有说共享传递啥的,在此我们不对这些字面进行讨论,我们就解释下本质到底是怎么个情况。
比如有如下代码
1 | var a = 1; |
这个就无需多解释了,x作为一个形参,只在函数内部生效,而且当a当作参数传给x的时候,其实就是复制了一份a指向的栈内存中存放的值,所以a和x就是毫无关系了
如果a为一个引用类型,如下代码
1 | var obj = {x : 1}; |
此处也一样,x作为一个形参,当obj当作参数传给o的时候,其实是复制了一份obj指向的栈内存中存放的对象的地址,所以obj和o在栈内存中是不一样的,但在栈内存中存的东西是一样的,就是对象{x : 1}所在的地址;
所以当修改o的时候,自然obj也就被修改了
还有人提出以下代码
1 | var foo = {name:'foo'}; |
这个结果是没问题的,但这就是上文提到的对对象的修改的方式的问题,是直接赋值去修改变量,还是去修改对象的属性;
上述代码foo当作参数传给o,同样将对象所在地址赋值一份给了形参o,但函数中o的赋值操作,使形参o完全指向了另外一个对象的内存地址,这个操作,并不影响实参foo在内存中存放的地址,及地址所指向的对象;