一、值类型和引用类型
简单数据类型又叫做基本数据类型或者值类型,复杂数据类型又叫做引用类型。
值类型:在存储时变量中存储的是值本身,因此叫做值类型
如:number、null、string、boolean、undefined。
注意:null比较特殊,它返回的是一个空的对象 object,如果有个变量我们以后打算存储为对象,但是暂时没想好放什么,就可以给它一个null。
var a = null;
console.log(typeof a);//输出为object
引用类型:在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型。
如通过new关键字创建的对象(系统对象、自定义对象),如Object、Array、Date等。
二、堆与栈
堆栈空间分配区别
1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈,简单数据类型存放到栈里面。
2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收,复杂数据类型存放到堆里面。
三、简单类型和复杂类型的内存分配
简单数据类型存放在栈里面,里面直接开辟一个空间存放的是值。
复杂数据类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中,即它是在栈里面存放一个地址,用十六进制表示,然后这个地址指向堆里面的数据。
下面我们举两个简单数据类型和复杂数据类型的内存分配情况来具体认识认识:
定义一个值类型:var age = 12;
定义一个数组(引用类型):var arr = [1,2,3];
四、简单数据类型传参
修改变量里的值时,直接在原来开辟的栈空间里修改。
如:var num = 10;
num = 20;
console.log(num);
函数的形参也可以看成是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做如何修改,都不会影响到外部变量。如:
function fn(a){
a++;
console.log(a);
}
var x = 10;
fn(x);
console.log(x);//输出为10
它的执行过程是先在栈里开辟一个空间,存入10,当我们执行函数fn(),把x作为参数传给函数里的形参a时,是在栈中新开了一个空间,把x的值复制了一份给形参,然后执行a++,让a的值变为11,最后当我们再输出x值时,整个代码只是形参改变了,x并没有改变,还是10。
五、复杂数据类型传参
函数的形参可以看成是一个变量,当我们把引用类型变量传给形参,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个对地址,所以操作的是同一个对象。如:
function Person(name) {
this.name = name;
}
function f1(x) {
console.log(x.name);
x.name = '张学友';
console.log(x.name);
}
var p = new Person('刘德华');
console.log(p.name);
f1(p);
console.log(p.name);
//输出为:
刘德华
刘德华
张学友
张学友
它的执行过程是:先定义了一个构造函数Person,再定义了一个函数f1,然后通过new关键字定义复杂数据类型对象p,此时在栈中开辟了一个空间,存入的是p对象在堆中的地址,在堆中开辟了一个空间,存入p对象的值name = ‘刘德华’,执行console.log(p.name);输出刘德华,然后调用f1函数,把复杂数据类型p传给函数的形参x,所以此时在栈中新开辟了一个空间,把p存入的地址复制一份给了x,由于他们俩的内容一样,所以指向的是同一个在堆中的值,执行 console.log(x.name);时输出刘德华,下一步是把name的值改为张学友,所以堆中的name值改为张学友,执行console.log(x.name);后输出张学友。调用函数完毕后还有一步是console.log(p.name);由于x和p变量中存的地址相同,在堆中是对同一个对象进行操作,所以此时会输出张学友。