搜了一些教程,归类整理
栈(stack)和堆(heap)
- stack为自动分配的内存空间,它由系统自动释放;
- heap则是动态分配的内存,大小不定也不会自动释放
JS中有基本类型和引用类型
- 基本类型:Undefined、Null、Boolean、Number 和 String。存放在栈内存中的简单数据段,直接按值存放的。
- 直接访问
- 引用类型:存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况开进行特定的分配。
- 访问,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据
传值与传址
- 前面之所以要说明什么是内存中的堆、栈以及变量类型,就是为了更好的理解什么是“浅拷贝”和“深拷贝”。
- 基本类型与引用类型最大的区别实际就是传值与传址的区别
基本类型
var a = 1;
var b =a;
b=2;
console.log(a,b) // 1 2
引用类型
var c = [1,2,3,4];
var d =c;
d[0]=2;
console.log(c,d) // [2,2,3,4] [2,2,3,4]
声明一个基本类型,都会在栈里新开辟一个新空间,哪怕值一样,存放值,所以var a ,var b=a 时,有两个,之后改值是互不影响的。
但声明一个引用类型,变量是定义的地址指针,所以 var d= c,导致地址指针是一样的,都同时指向堆里的同一个存储空间
浅拷贝和深拷贝
浅拷贝的时候如果数据是基本数据类型,那么就如同直接赋值那种,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么对于浅拷贝而言就只能拷贝其引用,对象的改变会反应到拷贝对象上;但是深拷贝就会拷贝多层,即使是嵌套了对象,也会都拷贝出来。所以会有人粗略地说:浅拷贝是一层,深拷贝是多层。如果拷贝不彻底,改变新数组中嵌套的引用对象,原数据会改变。
浅拷贝
基本类型数据互不影响,虽然拷贝对象一开始新建,但浅拷贝里面的引用类型的地址指针是同一个
方法一
var obj1 = {
a:1,
b:true,
c:"hello world",
d:function(){
console.log("我是一个方法")
},
e:[1,2,3],
f:{
g:1,
h:"我是一个h",
i:{
j:2,
k:3
}
},
}
function shallowCopy(obj){
var objCopy ={};
for (var i in obj){
objCopy[i]=obj[i]
}
return objCopy
}
var obj2 = shallowCopy(obj1)
console.log(obj1,obj2)
如果我们改变值呢
obj2.a=2
obj2.b=false
obj2.e=[4,5,6]
obj2.f.g=3
console.log(obj1,obj2)
方法二
ES6中的Object.assign方法,Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
//target:目标对象。
//sources:任意多个源对象。
//返回值:目标对象会被返回
Object.assign(target, ...sources)
var obj2 = Object.assign({}, obj1)
深拷贝
新旧数据互不干涉
方法一
一层一层赋值,不建议
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = {
a: obj1.a,
b: obj1.b,
c: obj1.c
};
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 }
console.log(obj2);
// { a: 10, b: 100, c: 30 }
方法二
用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象
只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON
JSON.parse(JSON.stringify(obj))
var obj1 = {
a:1,
b:true,
c:"hello world",
d:function(){
console.log("我是一个方法")
},
e:[1,2,3],
f:{
g:1,
h:"我是一个h",
i:{
j:2,
k:3
}
},
}
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a = 20;
obj2.f.g =3
console.log(obj1,obj2);
console.log(obj1 === obj2);
// false
console.log(obj1.f === obj2.f);
// false
方法三
递归
function shallowCopy(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
shallowCopy(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
shallowCopy(obj, str);
console.log(str.a);
方法四
直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果
var obj1 = {
a:1,
b:true,
c:"hello world",
d:function(){
console.log("我是一个方法")
},
e:[1,2,3],
f:{
g:1,
h:"我是一个h",
i:{
j:2,
k:3
}
},
}
function shallowCopy(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
console.log(deepClone(obj1,{}))
改变值
var test = shallowCopy(obj1,{});
test.a=2
test.f.g=3