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 所以这部分要考虑
  • 区分数组对象的原因是是因为有些方法只有数组能用,给他拷贝成了对象就用不了

拓展:

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

相关推荐
柯一梦2 小时前
STL2--vector的介绍以及使用
开发语言·c++
小马_xiaoen2 小时前
Promise 从入门到精通:彻底解决前端异步回调问题!!!
前端·javascript
云霄IT2 小时前
go语言post请求遭遇403反爬解决tls/ja3指纹或Cloudflare防护
开发语言·后端·golang
自动化控制仿真经验汇总2 小时前
电子抑振控制实验中MATLAB+示波器的用法-PART-RIGOL-电磁制振
开发语言·matlab
jingling5552 小时前
uniapp | 基于高德地图实现位置选择功能(安卓端)
android·前端·javascript·uni-app
凯子坚持 c2 小时前
C++基于微服务脚手架的视频点播系统---客户端(3)
开发语言·c++·微服务
某公司摸鱼前端2 小时前
前端一键部署网站至服务器FTP
前端·javascript·uni-app
代码方舟2 小时前
Java后端实战:对接天远车辆过户查询API打造自动化车况评估系统
java·开发语言·自动化
麒qiqi2 小时前
从 C 基础到 ARM Linux 驱动开发:嵌入式开发核心知识点全解析
java·开发语言