上篇文章提到了引用值和原始值,今天便要花些时间来刨析一下这两个值:
原始值:
首先,先来说一下栈,栈的特点为先入后出。采用动态一维数组来存储栈。所谓动态,指的是栈的大小可以根据需要增加。
原始值位于栈内,也遵循先入后出的特点。重点是:赋值为copy的关系。
例如:
请大家看到这里停下来想一想,输出的a,b的值分别是什么?b是否会随着a的改变而改变?
试验后,想必都可以得到 20 10的结果,即b的值并没有随着a的改变而改变。这是为什么?
栈内存之间的赋值为copy的关系,b = a的过程相当于三步:取出a,copy10,把10再赋给b,这样b的值是a的值的副本。此刻无论再怎么改变a的值对b都并不受影响。
引用值位于堆内,类似于下图这样的过程,先入先出:
例如:定义一个引用值时会发生这样一个过程:例如 var arr = [1,2,3,4];,此时申请一个叫做arr的房间,重点来了:它的地址是存在栈内存,即它在栈内申请了一个名为arr的房间,假设地址为heap1;那么后面的[1,2,3,4]放在哪里呢?[1,2,3,4]放在堆内存中。总结来说:引用值的地址位于栈内存中,引用值的值位于堆内存中。这样说的话,前面所说的引用值位于堆内存,也就有些不确切了吧。让我们来举个例子:
var arr = [1,2,3];
var arr1 = arr;
arr1的值现在是多少呢?自然是[1,2,3],然而过程却没有我们想象的那么简单。var arr1时在占内存中申请了一个房间,将它的地址存入,假设为heap2,此时在栈内存中有两个房间,一个存入了heap1,另一个存入了heap2,数组arr的定义使heap1指向堆内存中的[1,2,3],由于语句arr1 = arr;的缘故,heap2也指向了[1,2,3],此时arr1的值便为[1,2,3]。那么咱们现在来换一种方式吧:
var arr = [1,2,3];
var arr1 = arr;
arr.push(4);
此时,arr1的值是多少呢?考虑一下现在已有的情况heap1和heap2都指向堆内存中的同一个房间,当房间里多出了一个数,arr1和arr自然也会跟着改变,所以arr1的的值现在是[1,2,3,4]。那么咱们再接着来换一种方式吧:
var arr = [1,2,3];
var arr1 = arr;
arr = [1,3];
根据上面的规律,arr1的值按理说应该是[1,3]吧,但真的是这样吗?显然不是。原因在于:arr = [1,3];这条语句相当于在堆内存中开辟出一间新的房间,arr在栈内的地址heap1改变指向,指到[1,3]的房间,然而此刻arr2的地址heap2依旧指向原来[1,2,3]的房间,所以此时,arr1并没有随着arr的改变而改变,arr1的值依旧为[1,2,3];
总结来说:1.原始值赋值后,一个值改变并不影响另一个值。
2.(1)引用值往里push的时候,不会发生新房间的创建,一个值的改变会影响另一个值随之改变。
(2)当引用值直接用赋值符号改变自己的值时(arr = [1,3]),相当于创建出新的房间,栈中的地址改变指向,一个值的改变并不影响另一个值的改变。