在开发中,我们常常混淆数据和变量,其实这是两个相互关联单又不同的概念,想弄清这两个个概念我们还需要从js数据存储时的内存结构来看
- 数据是什么? 变量是什么? 内存是什么?
- 数据 :
在编程语言中数据是存储在内存或硬盘中代表特定信息的。。。东西,它的本质是二进制编码;
数据的特点是可存储,可修改;
程序运行的本质是在内存中操作数据;
我们常说一切皆对象,有它的道理但细究的话也不全面,对象实际上是数据的一种容器,或者说是一系列有关系的数据的集合。我觉的更准确的说法是一切皆数据。 - 变量:
- 可变化的量, 由变量名和变量值组成,在js中变量名是字符串类型的
- 每个变量都对应的一块小内存, 变量名用来查找对应的内存, 变量值就是内存中保存的数据。
- 变量的值不会是一个对象,引用变量保存的是对象数据的在堆内存中的地址
- 内存
- 内存条通电后产生的可储存数据的空间(临时的)
- 内存产生和死亡: 内存条(电路版)==>通电==>产生内存空间==>存储数据==>处理数据==>断电==>内存空间和数据都消失
- 一块小内存的2个数据
- 内部存储的数据
- 地址值
- 内存分类
- 栈: 存储变量
- 堆: 存储对象
- 数据 内存 和变量的关系
- 内存用来存储数据的空间
- 变量是内存的标识,js引擎根据这个表示找到并操作内存中的具体数据
- 两个引用变量指向同一对象数据问题
var obj1 = {a:1};
var obj2 = obj1
此时两个变量指向同一对象,让我们来用其中一个变量操作这个对象
obj1.a = 2;
console.log(obj2.a); //2
结论一:2个引用变量指向同一个对象, 通过一个变量修改对象内部数据, 另一个变量看到的是修改之后的数据
obj1 = {a:3};
console.log(obj2.a); //2
结论二:2个引用变量指向同一个对象, 让其中一个引用变量指向另一个对象, 另一引用变量依然指向前一个对象
这两个结论看似简单,明了但我们很容易就在这上面栽跟头,让我们来看一个进阶应用
可以自己先做一下
var obj1 = {a:1};
var obj2 = obj1;
function fun(obj2) {
obj2 = {a:2};
}
fun(obj2);
console.log(obj2.a)
结果是 1 ,先明确函数内外的obj2是两个不同的变量,在传参时外层obj2将他保存的对象地址传给了内层obj2,此时它们只想同一个对象。之后在函数内部对内层的obj2进行了重新赋值。根据“两个引用变量指向同一个对象, 让其中一个引用变量指向另一个对象, 另一引用变量依然指向前一个对象”,可以得出外层obj2依然执行原来的对象。
- 传参问题
上一个案例设计的参数传递问题,尤其是实参为变量时的参数传递,下面我们专门讨论下这个问题
- 变量为基本变量: 此时的传递方式是值传递,也叫复制传递,变量做参数,传入的并不是变量本身,而是将变量的值复制一份赋给形参
var a = 1;
function fun (a){
a = 2;
}
fun(a);
console.log(a); //1;
同样的内部和外部的a,是两个变量,传参是只是将外部a的值复制赋给内部的a,此后两者便没有关联。- 变量为引用类型的传递
有人认为是值传递,有人认为是引用传递。其实都没错,只是说法不同。都是在说传递地址,前者的值说的变量保存的地址值,后者说的是对某一个对象数据的引用,其实说的是一件事。
更为准确的描述:引用类型变量传递的是该变量中保存的对应对象的内存地址。在传参完成后实参和形参就是两个指向同一对象的变量。
- 变量和数据的生命周期
- 前置知识:
- 1变量分局部变量和全局变量
- 2变量占用栈内存空间
- 3对象数据保存在堆内存中
函数执行完(函数的执行上下文出栈)后,它的局部变量占用栈内存会被释放(不考虑闭包)
全局变量占用的栈内存永远不会释放,引文全局的执行上下文始终在栈中(不会出栈)
引用变量所指向的对象数据,当不存在变量指向它时,会被释放
举一个简单但很能说明问题的例子
function fun(){
var a = {b:1}
}
fun();
函数fun入栈—>栈内存开辟一块空间给a变量—>堆空间开辟一块空间给对象{b:1},并把这块空间的地址保存在a变量中—>**fun出栈—>**a占用的栈内存自动释放—>没有变量指向{b:1}对象,该对象占用的空间被回收