JavaScript中存在两大数据类型:基本类型(栈内存中)和引用类型(堆内存)。引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中。
浅拷贝
创建新的数据,这个数据有着原始数据属性值的一份精确拷贝。如果是基本类型,拷贝的就是基本类型的值。输入属性是引用类型,拷贝的就是地址(字段的引用),即浅拷贝是拷贝一层。因此,对新对象的修改可能会影响到原始对象,因为它们是共享相同的引用。
实现浅拷贝的方法
1.循环遍历赋值
for…in…/for…of…/forEach循环赋值实现的同样也都是浅拷贝
2.数组Array方法
① concat(合并数组,返回新数组):空数组和需要拷贝的数组合并,返回新数组实现拷贝。
let arr = [1,2,{a:3,b:4}];
let concatArr = [].concat(arr);
② slice(返回截取指定位置的子数组):从0索引开始截取,返回新数组。
let arr = [1,2,{a:3,b:4}];
let sliceArr = arr.slice(0);
③ Array.from(ES6新增:类数组对象或者可遍历对象转换成一个真正的数组):
let arr = [1,2,{a:3,b:4}];
let fromArr = Array.from(arr);
3. object.assgin
object.assgin用于对象合并,将源对象的所有可枚举属性Copy到目标对象上,类似于数组的concat方法。可以将原对象与一个空对象进行合并,生成一些新的对象实现拷贝。
let obj = {
a:1,
b:2,
c:[3,4],
d:{d1:5,d2:6}
};
let newobj = Object.assign(obj,{});
4. JQuery.$extend():第一个参数设置false或者不加则为浅拷贝
$.extend(true,{},a,b)
true:是否深度拷贝,不加为false,浅拷贝,加了深拷贝
{}:将合并结果保存到新对象,这样原对象将不会发生改变
a:第一个合并的对象
b:第二个合并的对象
5. 使用拓展运算(…list)
const sourceObj = { name: 'John', age: 25 };
const targetObj = { ...sourceObj };
console.log(targetObj); // 输出:{ name: 'John', age: 25 }
深拷贝
深拷贝创建一个新的对象,并且递归地复制原始对象的所有字段和引用指向的对象,而不仅仅是复制引用本身。深拷贝会递归复制整个对象结构,包括对象内部的对象,确保新对象和原始对象之间的所有关系都是独立的。这意味着对新对象所做的修改不会影响到原始对象,因为它们拥有彼此独立的副本。
1.循环递归
普通的循环为浅拷贝,追其原因是因为拷贝的过程中遇到了对象类型然后只拷贝了对象的指向。所以循环递归的原理就是循环的过程中判断值的类型,如果为对象类型,则在对象类型的数据里面进行进一步循环赋值,直到循环对象里面没有对象类型的数据为止。
let obj = {
a:1,
b:[1,2,3],
c:{d:4,e:5}
};
function deepClone(newObj, obj){
for (let key in obj){
if(obj[key] instanceof Array){
newObj[key] = [];
deepClon(newObj[key], obj[key]);
} else if(onj[key] instanceof Object){
newObj[key] = {};
deepClon(newObj[key], obj[key]);
} else {
newObj[key] = obj[key];
}
}
}
改变原对象obj里面的数组和对象值,newObj里面对应位置的值不会产生改变,可以证明obj和newObj是完全独立的两个对象。
2.JSON.parse(JSON.stringify())
JSON.stringify可以将JS对象转化成JSON字符串;利用JSON.parse方法可以将JSON字符串转化成JS对象。
let obj = {
a:1,
b:undefined,
c:'d',
d:null,
e:[2,3,4],
f:{G:5,H:6}
}
let newObj = JSON.parse(JSON.stringify(obj))
但是JSON方法具有局限性:
①如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
②如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null;
③JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
④如果对象中存在循环引用的情况也无法正确实现深拷贝;
⑤如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
⑥如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式;
3. JQuery.$extend()
第一个参数设置true;
4.第三方库
lodash _.cloneDeep实现深拷贝
深拷贝和浅拷贝的应用场景
深拷贝的应用场景:
- 当需要创建一个对象的完全独立副本时,以防止对原始对象的修改。
- 在对象状态管理中,需要创建对象的副本以记录历史状态、实现撤销和重做等操作。
- 在数据变换和处理过程中,创建对象的副本以避免对原始数据的修改。
浅拷贝的应用场景:
- 当只需要复制对象的引用,而不需要创建对象的副本时。
- 在一些简单的数据处理场景中,浅拷贝可以更高效地完成任务。