前言
在js中涉及到深浅拷贝的时候,就会牵扯到堆和栈。
栈和堆
在编译期间,除了声明变量和函数,查找函数中的标识符这两项工作之外,还有内存分配,不同类型的数据会分配到不同的内存空间。
栈内存 举个栗子,就比如有一个书架,每本书对应一个书格,只有先拿出来才能再放进去,是一个先出后进的顺序。这个书架就可以比作栈内存。
- 栈内存中主要保存基本数据类型值和引用数据类型值的地址。
- 栈内存是存在大小限制和储存上限的,就是一简单存储。
- 栈内存是自动分配内存大小的,读取数据快,写入速度快,但是储存内容少,变量一旦不使用系统会自动清除掉释放内存。
堆内存 堆内存就好比一个百宝箱,这个时候我们不需要先出后进的操作,只需要知道我们想要什么东西,根据这个东西的名称直接去拿就行了。
- 主要保存一组无序且唯一的引用数据类型值,可以使用栈内存中的键名来获取。
- 堆内存的存储数据多,读写速度慢,一般对象会储存在堆中,对内存的大小和限制都是未知的。
- 堆内存是动态分配内存的,也不一定会自动释放内存。
赋值和赋址
先来简单说一下 赋值指的就是把堆内存中的对象数据获取到存放到栈内存中,就相当于在栈内存中新创建了一个值然后把该值赋给新创建的变量;
赋址指的就是把堆内存的数据值存放到栈内存中,并且地址也是指向堆内存的,就相当于在栈内存中创建一个指针,这个指针指向堆内存中的值。
看段代码:
function a(){
var a=[1,2,3,4,5];
var s=a;
var d=a[2];
console.log(s);
console.log(d);
s[0]=6;
d=7;
console.log(s);
console.log(d);
console.log(a);
}
从上面的代码我们可以知道,改变了s的数据,同时会改变堆中a的对应值;改变d的值,堆中a的对应值是不会做出改变的。
这就是赋值和赋址的区别。
浅拷贝和深拷贝
所谓拷贝,就是赋值的意思;把一个值赋值给另一个值。
基本数据类型赋值的时候,赋值的是数据,所以并不存在浅拷贝和深拷贝的问题。
浅拷贝
创建一个新的对象值,给这个对象赋原始对象的属性值,指向不同的地址。也就是说,浅拷贝是拷贝的原始对象的属性值,如果拷贝的这个值是引用类型,拷贝的就是原始对象的地址,拷贝的不深所以叫浅拷贝。
- 拷贝原对象中的基本数据类型值,是不会改变原对象的。
- 如果拷贝的是原对象中子对象的引用数据类型值,是会改变原对象的值;因为在堆中,对象里面的子对象会和主对象共享地址,这个时候原对象也会跟着改变。
栗子:
1,使用for…in…实现浅拷贝
function s(){
var _data={
id:1,
num:10,
list:{
id:1,
num:1
}
};
var _newData={};
for(var i in _data){
_newData[i]=_data[i];
}
_newData.id=2;
_newData.list.id=2;
console.log('_data值:',_data)
console.log('_newData值:',_newData)
}
从面的栗子可以很清楚的看到,浅拷贝确实是满足我们上面说的两个小点。
2,使用Object.assign()实现浅拷贝
function d(){
var _data={
id:1,
num:10,
list:{
id:1,
num:1
}
};
var _newData={};
Object.assign(_newData,_data)
_newData.id=2;
_newData.list.id=2;
console.log('_data值:',_data)
console.log('_newData值:',_newData)
}
这个方法相对于for…in…简单很多。
深拷贝
深拷贝不仅仅只是把原对象赋值给新对象,并且还会把原对象中子对象都原模原样赋值给新对象,这样修改新对象后就不会改变原对象的值。
栗子:
1,使用JSON.stringify()实现深拷贝
需要注意的是这种方法无法拷贝,正则表达式,undefined,function
function f(){
var _data={
id:1,
num:10,
list:{
id:1,
num:1
}
};
var _newDataStr=JSON.stringify(_data),
_newData=JSON.parse(_newDataStr);
_newData.id=2;
_newData.list.id=2;
console.log('_data值:',_data)
console.log('_newData值:',_newData)
}
2,使用递归的方式实现深拷贝
function g(data) {
if (typeof data != "object") {
return data
}
var _newData = Array.isArray(data) ? [] : {};
if (data instanceof Array) {
console.log("是数组")
for (var i = 0; i < data.length; i++) {
_newData[i] = data[i]
if (typeof _newData[i] == 'object') {
g(_newData[i]);
}
}
} else {
console.log("是对象")
for (var i in data) {
if (typeof data[i] == 'object') {
_newData[i] = g(data[i])
} else {
_newData[i] = data[i]
}
}
}
return _newData
}
console.log(g({ a: 1, s: 2, d: [1, 2, 3] }));
|