栈:
队列优先,先进先出,由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
堆:
先进后出,动态分配的空间一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(Operatig System,管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件)回收,分配方式类似于链表
基本类型:
存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配
5种基本数据类型:undefined,Null,Boolean,Number和String,它们是直接按值存放的,所以可以直接访问
引用类型:
存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况进行特定的分配。
当我们需要访问引用类型(如对象,数组,函数等)的值,首先从栈中获得该对象的地址指针,然后从堆内存中取得所需要的数据
实例一:js中创建两个对象p1,p2,使用赋值法:
function Person(){//创建一个构造函数
this.name = 'zhangsan',
this.age = 30
}
var p1 = new Person();//初始化一个对象,并不需要返回这个对象,自动返回这个对象
var p2 = p1;//此时复制堆上p1的地址址给p2,所以现在p1,p2指向堆上同一内存空间
console.log(p2.name);//输出zhangsan
p2.name = "apple";//此时修改的是p2地址指向的堆上对象的属性值
console.log(p1.name);//输出appple
p2 = null;
//console.log(p2.name);报错,无法找到name这个属性
console.log(p1.name);//输出apple
实例二:
var a = [1,2,3,4,5];
var b =a;//此时变量a,b指向同一个堆上的对象
var c = a[0];
console.log(b);//弹出1,2,3,4,5
console.log(c);//弹出1
b[4] = 6;//改变的栈上对象
c = 7;
console.log(a[4]);
console.log(a[0]);
当改变b数据,a中数据对应也发生了变化,但是改变c中数据时,a的值并没有发生变化,这就是传值与传址的区别,因为a是数组型,属于引用型,所以它赋给b的时候传的是栈中的地址(相当于新建了一个不同名的“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取了一个数据值,并保存在栈中。所以当b修改时,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中
在定义一个数组或者对象,变量存放的往往只是一个 地址。当我们使用对象拷贝时,如果属性是对象或者数组时,这时候我们传递的只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象的堆内存中,即意味着父子对象发生了关联,两者的属性值会指向同一个内存空间
var a = {
name : "JavaScript"
}//使用对象直接量创建一个对象
function Copy(p){
var c = {};//创建一个空对象
for(var i in p){//for...in遍历传入对象参数p中所有的属性值
c[i] = p[i];
}
return c;
}
a.author = {
firstname:"David",
surname:"Flanagan"
}//给a新增了一个属性,此属性是一个对象
var b = Copy(a);
b.key3 = '33333';
console.log(b.name);//输入JavaScript
console.log(b.key3);//输出33333
console.log(a.key3);//输出undefined 4
a对象name是字符串,author是一个对象,当把a拷贝到b中,1,2属性顺利的拷贝到b对象中去,给b对象新增一个字符串类型的属性,b能正常修改,但是a中并无定义,说明了对象的key3(基本类型)并没有关联到父对象中去,所以是undefined
但是
b.author ={
name:dadiaoge;
}
console.log(a.author);
console.log(b.author);//两者都输出改变后的值
所以,当修改的属性变为对象或者数组时,那么父子对象之间会发生关联,即对b对象属性进行修改时,a,b 的author属性值均发生了改变,其在内存中的状态可以用以下图片来表示:
name 属于基本类型,所以拷贝的时候传递的是该数据段。但是author属性是一个对象值,所以author在拷贝的时候传递的是指向该对象的地址,无论复制多少个此属性,其值始终指向父对象author在堆上的内存空间
在实际情况中,我们在编码的时候并不希望父子对象之间发生关联,这时候可以用到深度拷贝,既然属性值是对象或者数组的时候,只会传址。那么我们使用递归来解决问题,把所有父对象中所有属于对象的属性类型都遍历赋给子对象就好
function Student(){
this.name = "zhangsan",
this.age = 12,
this.grade = {
English : "good",
math : "so"
}
}
var a = new Student;//实例化一个对象
function Copy(p,c){
var c = c || {};//创建一个空对象
for(i in p){//变量p中的属性
if(typeof p[i] === 'object'){//如果p中的这个属性的类型是对象
c[i] = (p[i].constructor === Array)?[]:{};//判断该属性的类型是不是数组类型,判断完毕返回一个类型
Copy(p[i],c[i]);//判断结束完毕之后,遍历p此属性中的属性值并将它赋给新创建的对象的这个属性
}
else{
c[i] = p[i];
}
}
return c;
}
var b = {};
b = Copy(a,b);
b.aihao = {
English:"great"
}
console.log(a);//不改变其原有属性的值(可以通过打断点来自己看一下整个流程)