JSON数据的深拷贝与浅拷贝实现方法详解
在JavaScript开发中,拷贝对象或数组是常见操作,而JSON(JavaScript Object Notation)作为轻量级的数据交换格式,其数据的拷贝方式尤其值得关注,深拷贝与浅拷贝的核心区别在于是否复制引用指向的值本身,而非引用地址,本文将结合JSON数据的特点,详细解析浅拷贝与深拷贝的实现原理及具体方法。
浅拷贝:复制引用,共享底层值
浅拷贝(Shallow Copy)只复制对象或数组的第一层属性,对于值为基本类型(string、number、boolean、null、undefined)的属性,会直接复制其值;而对于值为引用类型(对象、数组、函数等)的属性,则复制其引用地址,导致拷贝后的数据与原数据共享同一底层值。
JSON数据浅拷贝的实现方法
使用 Object.assign() 或展开运算符()
Object.assign() 用于将一个或多个源对象的属性复制到目标对象,展开运算符则可以快速展开数组或对象的元素,由于它们默认只进行第一层拷贝,因此适用于JSON数据的浅拷贝。
const originalJson = {
name: "Alice",
age: 25,
address: {
city: "Beijing",
district: "Haidian"
},
hobbies: ["reading", "coding"]
};
// 方法1:Object.assign()
const shallowCopy1 = Object.assign({}, originalJson);
// 方法2:展开运算符
const shallowCopy2 = { ...originalJson };
// 修改浅拷贝后的第一层数据
shallowCopy1.age = 30;
shallowCopy2.name = "Bob";
console.log(originalJson.name); // "Alice"(原数据name未变)
console.log(originalJson.age); // 25(原数据age未变)
// 修改浅拷贝中的引用类型属性
shallowCopy1.address.city = "Shanghai";
shallowCopy2.hobbies.push("gaming");
console.log(originalJson.address.city); // "Shanghai"(原数据address被共享)
console.log(originalJson.hobbies); // ["reading", "coding", "gaming"](原数据hobbies被共享)
使用 Array.prototype.slice() 或 Array.prototype.concat()
对于JSON数组,slice()(无参数)和 concat()(无参数)会返回新数组,但数组中的引用类型元素仍共享原数据。
const originalArray = [
{ id: 1, name: "item1" },
{ id: 2, name: "item2" }
];
const shallowCopyArray = originalArray.slice();
shallowCopyArray[0].name = "modifiedItem1";
console.log(originalArray[0].name); // "modifiedItem1"(引用类型元素被共享)
深拷贝:递归复制,完全独立
深拷贝(Deep Copy)会递归复制对象或数组中的所有层级属性,确保拷贝后的数据与原数据完全独立,修改拷贝数据不会影响原数据,反之亦然,JSON数据通常结构清晰(不包含函数、Symbol、undefined等特殊类型),因此是实现深拷贝的理想场景。
JSON数据深拷贝的实现方法
使用 JSON.parse(JSON.stringify())(经典方法)
这是最常用的JSON数据深拷贝方式:先将原数据通过 JSON.stringify() 转换为JSON字符串,再通过 JSON.parse() 将字符串解析为新的对象或数组,由于JSON字符串是原始文本,解析后的对象与原数据完全独立。
const originalJson = {
name: "Alice",
age: 25,
address: {
city: "Beijing",
district: "Haidian"
},
hobbies: ["reading", "coding"],
// 注意:function、Symbol、undefined等会被忽略或转为null
sayHi: function() { console.log("Hi"); },
[Symbol("id")]: 123,
data: undefined
};
const deepCopy = JSON.parse(JSON.stringify(originalJson));
// 修改深拷贝数据
deepCopy.age = 30;
deepCopy.address.city = "Shanghai";
deepCopy.hobbies.push("gaming");
console.log(originalJson.age); // 25(原数据未变)
console.log(originalJson.address.city); // "Beijing"(原数据未变)
console.log(originalJson.hobbies); // ["reading", "coding"](原数据未变)
// 检查特殊类型的处理
console.log(deepCopy.sayHi); // undefined(function丢失)
console.log(deepCopy[Symbol("id")]); // undefined(Symbol丢失)
console.log(deepCopy.data); // null(undefined转为null)
优点:代码简洁,无需递归,适合纯JSON数据(无函数、Symbol、循环引用等)。
缺点:
- 会丢失
function、Symbol、undefined、RegExp等特殊类型; - 无法处理循环引用(如
obj.self = obj,会报错Uncaught TypeError: Converting circular structure to JSON); - 会忽略对象的
prototype和constructor属性。
递归实现深拷贝(通用性强)
对于包含非JSON类型(如函数)或循环引用的数据,可通过递归方法实现深拷贝,递归的核心逻辑是:遍历对象或数组的每一层属性,若为基本类型则直接复制,若为引用类型则递归调用拷贝函数。
function deepClone(obj, hash = new WeakMap()) {
// 处理基本类型、null、function、Symbol等
if (obj === null || typeof obj !== "object") {
// function、Symbol直接返回(也可根据需求拷贝)
if (typeof obj === "function") {
return obj.bind({}); // 绑定新上下文,避免this指向问题
}
return obj;
}
// 处理循环引用:若已拷贝过,直接返回缓存的对象
if (hash.has(obj)) {
return hash.get(obj);
}
// 根据类型创建新对象/数组
const cloneObj = Array.isArray(obj) ? [] : {};
hash.set(obj, cloneObj); // 缓存当前对象,防止循环引用
// 递归拷贝所有属性
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
// 测试递归深拷贝(含函数和循环引用)
const originalObj = {
name: "Alice",
sayHi: function() { console.log("Hi, " + this.name); },
friend: null
};
originalObj.self = originalObj; // 循环引用
const deepCloneObj = deepClone(originalObj);
deepCloneObj.name = "Bob";
deepCloneObj.sayHi(); // "Hi, Bob"(函数独立)
console.log(deepCloneObj.self === deepCloneObj); // true(循环引用正确处理)
console.log(originalObj.self === originalObj); // true(原数据循环引用未破坏)
优点:支持函数、Symbol、循环引用等复杂场景,可定制化程度高。
缺点:代码较复杂,需手动处理多种数据类型,递归过深可能导致栈溢出(可通过循环+栈优化)。
使用第三方库(如Lodash)
Lodash的 _.cloneDeep() 方法是成熟、高效的深拷贝方案,支持复杂类型和循环引用,适合生产环境使用。
const _ = require("lodash");
const originalJson = {
name: "Alice",
address: { city: "Beijing" },
hobbies: ["reading"],
self: null
};
originalJson.self = originalJson; // 循环引用
const deepCopy = _.cloneDeep(originalJson);
deepCopy.address.city = "Shanghai";
console.log(originalJson.address.city); // "Beijing"(原数据未变)
console.log(deepCopy.self === deepCopy); // true(循环引用正确处理)
优点:稳定、高效,支持多种数据类型和边缘场景,无需手动实现。
缺点:需引入第三方库,增加项目体积(可通过按需引入优化)。
深拷贝与浅拷贝的选择依据
| 场景 | 推荐拷贝方式 | 原因 |
|---|---|---|
| 数据为纯JSON(无函数、循环引用) | JSON.parse(JSON.stringify()) |
简单高效,无需额外依赖 |
| 数据含函数、Symbol、循环引用 | 递归深拷贝或Lodash的 _.cloneDeep() |
避免数据丢失,正确处理复杂场景 |
| 只需修改顶层属性,不涉及嵌套引用 | 浅拷贝(Object.assign、展开运算符) |
性能更高,内存占用更小 |
| 大数据量拷贝且对性能要求高 |



还没有评论,来说两句吧...