首先我们要先了解js的数据类型(8大数据类型)
分为基本数据类型:String、Number、BigInt、null、undefined、、Boolean、Symbol(ES6中引入的一种新的基本数据类型,用于表示一个独一无二的值)。
注:BigInt在谷歌67版本中出现。是指安全存储、操作大整数。(但是很多人不把这个做为一个类型)。
复杂数据类型(引用数据类型):Object(对象、数组、函数)
接下来我们先来看看栈和堆:
栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS(垃圾回收机制)回收,分配方式倒是类似于链表。
接下来来看看基本数据类型和复杂数据类型是咋存储的吧!
基本数据类型值存储在栈中!!!如下图所示:
<body>
<script>
var n=10;
var num=n;
</script>
</body>
复杂数据类型引用地址存储在栈中内容存储在堆中!!!如下图所示:
var arr1=[1,2,3,4,5]
var arr2=arr1;
了解过上面的知识后,来看看深拷贝和浅拷贝吧!(内心os:终于到这了!)
基本数据类型的拷贝应该为引用赋值没有深拷贝和浅拷贝之说!
对于深拷贝网上有的解释为新的对象(或数组)改变值会不会影响原始对象(数组),如果影响则为浅拷贝,不影响则为深拷贝。
本质上我的理解应该是对于复杂数据类型的拷贝中是否存储数据的堆内存得到了拷贝。
浅拷贝:只在栈中拷贝了一层一摸一样的栈内地址,相同的栈内地址指向的是同一段堆地址,所以新对象(或数组)对内容的操作改变的共用堆内存的东西,进而也会影响原来的对象(或数组)。
浅拷贝:栈内拷贝了栈内地址,堆内也拷贝了一份新的地址,新对象(或数组)对内容的操作改变新拷贝的那一份堆内存存储的的东西,不会影响原来的对象(或数组)。
来看看这几个方法吧!(面试重点): 面试官:数组中浅拷贝的方法有哪些?
数组:
1、slice
var arr1=[1,2,3,4];
var arr2=[1,[1,2],3,8];
var arr3=arr1.slice();
var arr4=arr2.slice();
arr3[3]=100;
arr4[1][0]=200;
console.log(arr1[3])//4
console.log(arr3[3]);//100
console.log(arr4[1][0]);//200
console.log(arr2[1][0]);//200
2、concat
var arr1=[1,2,3,4];
var arr2=[1,[1,2],3,8];
var arr3=[].concat(arr1);
var arr4=[].concat(arr2)
console.log(arr2[1][0]);
arr3[3]=100;
arr4[1][0]=200;
console.log(arr1[3])
console.log(arr3[3]);
console.log(arr4[1][0]);
console.log(arr2[1][0]);
3、[...]
var arr1=[1,2,3,4];
var arr2=[1,[1,2],3,8];
var arr3=[...arr1];
var arr4=[...arr2];
console.log(arr2[1][0]);
arr3[3]=100;
arr4[1][0]=200;
console.log(arr1[3])
console.log(arr3[3]);
console.log(arr4[1][0]);
console.log(arr2[1][0]);
4、form
var arr1=[1,2,3,4];
var arr2=[1,[1,2],3,8];
var arr3=Array.from(arr1);
var arr4=Array.from(arr2);
console.log(arr2[1][0]);
arr3[3]=100;
arr4[1][0]=200;
console.log(arr1[3])
console.log(arr3[3]);
console.log(arr4[1][0]);
console.log(arr2[1][0]);
对象:
1、assign();
let oldObj = {
inObj: {a: 1, b: 2},
c:1
}
let newObj = Object.assign({}, oldObj)
newObj.inObj_2 = {a: 1, b: 2}
newObj.c=2;
newObj.inObj.a = 2
console.log(oldObj) // { inObj: { a: 2, b: 2 } }
console.log(newObj) // { inObj: { a: 2, b: 2 }, inObj_2: { a: 1, b: 2 } }
2、[...]
let oldObj = {
inObj: {a: 1, b: 2}
}
let newObj = {...oldObj}
newObj.inObj_2 = {a: 1, b: 2}
newObj.inObj.a = 2
console.log(oldObj) // { inObj: { a: 2, b: 2 } }
console.log(newObj) // { inObj: { a: 2, b: 2 }, inObj_2: { a: 1, b: 2 } }
? ? ?有没有疑问???
这几种方法严格上来说是浅拷贝,但当数组(或对象)为单层时对于单层的数据表现出来的是深拷贝,对于双层或多层内的数据仍然为浅拷贝!
-----------------------------------------------------深拷贝来了-------------------------------------------------------------
1、JSON.stringify、JSON.parse(数组对象都可用这里写了一中)
const oldObj = {
name: 'A',
name1: {value:3}
}
const newObj = JSON.parse(JSON.stringify(oldObj));
newObj.name = 'B'
newObj.name1.value = 4
console.log(oldObj)
// {name: 'A',name1: { value: 3 }}
console.log(newObj)
{ name: 'B', name1: { value: 4 } }
这种方法有一个弊端:无法识别function和undefined和sysmbol
2、lodash.js第三方库: _.cloneDeep( )
const _ = require('lodash');
let oldObj = {
a: 1, b: 2,c:{value:3}
}
let newObj = _.cloneDeep(oldObj)
newObj.a = 2
newObj.c.value = 4
console.log(oldObj) // { a: 1, b: 2, c: { value: 3 } }
console.log(newObj) // { a: 2, b: 2, c: { value: 4 } }
有没有完美的办法呢?
大佬来了!!!~~递归!!
<html>
<body>
<script>
function deepCopy(target) {
let result;//设置返回值
if(typeof target ==="object"){//判断传入数据是否为引用类型
if(Array.isArray(target)){//判断数据是否为数组
result = [];
for(let ele of target){//遍历数组
result.push(deepCopy(ele));//对每个数组值再次使用深拷贝函数
}
}else{//传入数据为对象类型
result = {};
for(let ele in target){//遍历对象
result[ele] = deepCopy(target[ele]);//对每个属性再次使用深拷贝函数
}
}
}else{//传入数据为基本类型,直接返回传入值
result = target;
}
return result;
}
</script>
</body>
</html>