JavaScript 中浅拷贝与深拷贝的差异与实现方式整理

文章目录

  • [JavaScript 中浅拷贝与深拷贝的差异与实现方式整理](#JavaScript 中浅拷贝与深拷贝的差异与实现方式整理)

JavaScript 中浅拷贝与深拷贝的差异与实现方式整理

浅拷贝是指复制值时,满足对象A 与物件B 不同,但物件A 与物件B 有相同的属性,并且属性的原型链相同。


而深拷贝则是指在拷贝时,对象A 与对象B 不同,两者在原型链上仅是结构相同,但其属性实际的地址不同。在拷贝值时,有可能会遇到变数是多层的情境,例如是一个对象里还有对象,深拷贝的定义会是每一层的值都不会共享址(reference)。

这样听起来可能比较抽象,具体来说,以lodash 这个套件提供的效用函式为例,有分成clonecloneDeep两种不同效用函式,clone只用于浅拷贝(第一层拷贝),但cloneDeep可用于深拷贝。下面的例子说明两者的区别:

javascript 复制代码
// lodash 的浅拷贝 clone
var objects = [{ a: 1 }, { b: 2 }];
var shallow = _.clone(objects);
console.log(objects === shallow); // false
console.log(shallow[0] === objects[0]); // true

// lodash 的深拷贝 cloneDeep
var objects = [{ a: 1 }, { b: 2 }];
var deep = _.cloneDeep(objects);
console.log(objects === deep); // false
console.log(deep[0] === objects[0]); // false

1.手写浅拷贝(shallowcopy)

方法一:手动复制值

javascript 复制代码
let objA = {
  a: 1,
  b: { c: 3 },
};

let objB = { a: objA.a, b: objA.b };

console.log(objA === objB); // false
console.log(objA.b === objB.b); // true, 第二层的对象是指向相同位置

方法二:使用展开参数

javascript 复制代码
let objA = {
  a: 1,
  b: { c: 3 },
};

let objB = { ...objA };

console.log(objA === objB); // false
console.log(objA.a === objB.a); // true, 第二层的对象是指向相同位置
console.log(objA.b === objB.b); // true, 第二层的对象是指向相同位置

方法三:使用Object.assign

javascript 复制代码
let objA = {
  a: 1,
  b: { c: 3 },
};

let objB = Object.assign({}, objA);

console.log(objA === objB); // false
console.log(objA.b === objB.b); // true, 第二层的对象是指向相同位置

2.手写深拷贝(deep copy)

方法一:使用JSON.parse(JSON.stringify(...))

前置知识:

1.全局内建工具函数

JSON 是 JS 全局内建工具对象,类似于 Math

2.序列化

序列化(serialization)

就是把内存里的复杂数据结构

转换成一种 "可以存、可以传、可以写成字符串的形式"

例如内存中的对象:

javascript 复制代码
const obj = {
  name: 'Tom',
  age: 18,
  hobbies: ['code', 'music']
};

序列化后:

javascript 复制代码
{"name":"Tom","age":18,"hobbies":["code","music"]}

3.有些数据类型不能被序列化,会丢失数据

类型 结果
function 丢失
undefined 丢失
Symbol 丢失
Date 变字符串
Map / Set 变空对象
循环引用 直接报错

这个做法是先将对象用JSON.stringify序列化为string,再通过JSON.parse转换回物件。要特别注意,这做法只能用于可序列化的物件,有些无法序列化的物件例如:function、HTML 的元素,这些是无法序列化的,所以执行前,需要先确认是否可以序列化,否则在执行JSON.stringify时会失败。

javascript 复制代码
let objA = {
  a: 1,
  b: { c: 3 },
};

function deepCopy(item) {
  return JSON.parse(JSON.stringify(item));
}

let objB = deepCopy(objA);

console.log(objA === objB); // false
console.log(objA.b === objB.b); // false

方法二:使用structuredClone(value)

针对可序列化的物件,有另外一种透过JavaScript 内建的方法达成深拷贝。这种方法是structuredClone(value),用法如下。

javascript 复制代码
let objA = {
  a: 1,
  b: { c: 3 },
};

let objB = structuredClone(objA);

console.log(objA === objB); // false
console.log(objA.b === objB.b); // false

方法三:手动递归式深拷贝

初级版本:适合面试

javascript 复制代码
//目的:深拷贝,不共用地址,自己用自己的地址,确保新旧对象之间完全独立
// 递归出口
//如果不是对象数组,就直接返回,直接拿到对应的值
//如果是对象数组,就可能是嵌套的结构,就需要递归遍历,处理每一层嵌套

// 遍历
// 循环当前对象或者数组中的每一项,依次赋值返回
function deepClone(obj)
{
    // 递归出口
    if(typeof obj===null || typeof obj !== 'object') return obj;
    const clone =Array.isArray?[] :{}

    for(let key in obj)
    {
        clone[key]=deepClone(obj[key])
    }

    return clone
}

const original = {
            name: "Alice",
            age: 25,
            info: {   //information 的简写
                city: "Beijing"
            }
        };


var deep=deepClone(original);
deep.info.city="xian";
console.log(original.info.city); //Beijing
console.log(deep.info.city);// xian

解析:

  • typeof 数组 对象 null 的时候结果都是 object 所以这部分要考虑
  • 区分数组对象的原因是是因为有些方法只有数组能用,给他拷贝成了对象就用不了

拓展:

这个版本能处理普通对象和数组,但不支持循环引用和特殊对象

相关推荐
Wenweno0o13 小时前
0基础Go语言Eino框架智能体实战-chatModel
开发语言·后端·golang
@yanyu66613 小时前
07-引入element布局及spring boot完善后端
javascript·vue.js·spring boot
chenjingming66613 小时前
jmeter线程组设置以及串行和并行设置
java·开发语言·jmeter
@大迁世界13 小时前
2026年React大洗牌:React Hooks 将迎来重大升级
前端·javascript·react.js·前端框架·ecmascript
cch891813 小时前
Python主流框架全解析
开发语言·python
不爱吃炸鸡柳14 小时前
C++ STL list 超详细解析:从接口使用到模拟实现
开发语言·c++·list
十五年专注C++开发14 小时前
RTTR: 一款MIT 协议开源的 C++ 运行时反射库
开发语言·c++·反射
Momentary_SixthSense14 小时前
设计模式之工厂模式
java·开发语言·设计模式
风止何安啊14 小时前
为什么要有 TypeScript?让 JS 告别 “薛定谔的 Bug”
前端·javascript·面试
‎ദ്ദിᵔ.˛.ᵔ₎14 小时前
STL 栈 队列
开发语言·c++