无论是何种编程语言,Java、JavaScript、Python等。代码的最终执行者都是计算机的中央处理器(CPU)。代码一开始存在在计算机的磁盘(硬盘)中,执行时先由硬盘加载到内存,在从内存加载到CPU,由CPU来执行程序的运算。
所有的代码如果要执行,必须先加载到内存中!那么变量和值在内存中是如何存在的呢?首先你要把内存想象成是一个大大的盒子,很大很大,在它的内部可以装很多很多的东西。
假设现在有一个数值35,这个值在内存中要如何存储呢?首先在内存中会单独开辟出一块空间,这块空间专门用来存储35这个值。
每一块内存空间都是独一无二的,每一块内存空间都有唯一的内存地址。上例中的35所在的内存地址为0x123(这个地址在这里只是一个假设的值,真实地址可能要长一些,0x表示十六进制)。
那么变量又在哪里呢?假设我的代码是let a = 35,这在内存中要如何体现呢?在内存中会有块专门存储变量的区域,这块区域被称为被称为变量对象。
上图中左侧的灰色区域就是变量对象,现在变量对象中就存储了两个变量a和b,那么变量如何和值建立联系呢?为什么通过变量a就能找到值35呢?上文我们说到每一块内存都有一个唯一的内存地址,通过这个地址可以找到该值,变量会将值的内存地址存储起来。上例中a存储的地址为0x123、b存储的地址为0x223。当使用变量a时,便可以通过a中存储的地址来找到它所指向的变量。
还要强调一点,这里我展现的内存模型是一个简单版的。内存中的一些细节被省略了,但是并不影响我们去理解值和变量之间的关系。
那么一个对象在内存中是如何保存的呢?其实是类似的,假设有如下代码let c = {name:'swk',age:35,gender:'male'}
,我们来看看它在内存中是存储的。
对象的结构会更复杂一些,其实对象和变量对象在结构上是一致的,都是对象嘛!不同点在于变量对象中存储的数据叫变量,而对象中存储的数据叫做属性。
对象同样也有一个唯一的内存地址,在变量c中存储了对象(0x323)的内存地址。对象(0x321)中存储了三个属性name、age还有gender。同样的,属性中并没有存储值,而是存储内存地址。name存储的是’swk’的内存地址,age存储的是35的内存地址,gender存储的是male的内存地址。
当我们访问属性时,比如这样:c.name
。首先使用了变量c,浏览器会先通过变量c找到它存储的内存地址0x323,从而找到了对象(0x323),然后通过name属性找到一个内存地址0x423,最后找到0x423找到其对应的字符串’swk’。
现在你大概能明白变量和值之间的关系了,在JS中无论是何种类型的数据,在变量中存储的都是其内存地址,使用变量只是通过内存地址找到它所对应的值,所以我们之前说过变量并不直接存储值,而更像是一个值的别名。
之前我们所提到的不可变值,说的是值不可变,值也就是图中橙色的部分。
但是对象和其他类型的值不太一样,对象属于可变类型。可以修改对象的的属性值,比如修改对象(0x323)的age属性值修改为45(c.name = 45
),这其实就是将原来对象(0x323)中name属性的值从0x123变为0x223即可。
简单总结,如果内存中是直接存储值的话(数字、字符串、布尔值、空值、未定义)是不能修改的,值一旦被创建就不能修改。如果存储的是内存地址,通常是可以修改的。
常量
const关键字用来声明一个常量,常量也是一个变量,只是常量一旦建立值变不能再修改,比如:
现在常量就比较好理解了,常量一旦设置,则会将变量和内存地址进行了绑定,将该变量和值(内存地址)变得不可分离。上例中创建变量a时,将其的值设置为10,这时a中已经存储了10的内存地址。接着a = 20,这步是希望a中存储的内存地址变为20的内存地址,但是由于a是常量,所以不好意思,改不了,于是报错了。
常量只会对变量对象产生影响,并不会影响到对象。这意味着如果你声明了一个常量,常量指向了一个对象,此时无法修改变量的所指向的对象,但并不会影响对象中属性的修改。
const o = {name:'swk'}
o.name = 'zbj' // 修改对象的属性
总之声明常量只是让变量不可变,不会导致对象中的属性不可变。所以还是强调一点,修改一个值时要能够区分出到底是在改变量还是在改对象!
什么是改变量?什么又是改对象呢?好好想想吧!
超老师有个问题,
假如一个程序的多个变量都使用同一个字面量,是不是这多个变量都是共用同一个内存地址。
是的
谢谢超老师,纠正了我之前对变量在内存中是如何存储的错误认识。