首页 > 其他分享 >浅拷贝与深拷贝 必须拿下!

浅拷贝与深拷贝 必须拿下!

时间:2024-08-09 14:25:42浏览次数:14  
标签:obj1 obj age let details 必须 拿下 拷贝

在 JavaScript 中有不同的方法来复制对象。但主要还是这两种——深拷贝与浅拷贝。这也会是面试的高频考点。因此,本文将带你深入理解深拷贝与浅拷贝,一篇文章足以学透彻!

文末有我在前端面试多年的经验文章,分享给大家!!!

浅拷贝与深拷贝

浅拷贝

浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址。所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

let obj1 = { name: '张三', details: { age: 25, address: '上海' } };
let obj2 = shallowClone(obj1);
obj2.details.age = 30;
console.log(obj1.details.age); // 30
console.log(obj2.details.age); // 30

function shallowClone(source) {
    let target = {};
    for (let key in source) {
        if (source.hasOwnProperty(key)) {
            target[key] = source[key];
        }
    }
    return target;
}

深拷贝

深拷贝是将一个对象从内存中完整地拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

let obj1 = { name: '张三', details: { age: 25, address: '上海' } };
let obj3 = deepClone(obj1);
obj3.details.age = 30;
console.log(obj1.details.age); // 25
console.log(obj3.details.age); // 30

function deepClone(obj) {
    if (obj === null) return obj;
    if (typeof obj !== "object") return obj;
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    let cloneObj = new obj.constructor();
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            cloneObj[key] = deepClone(obj[key]);
        }
    }
    return cloneObj;
}

借助以下的图片,帮我们更好地理解两者的含义:

总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。因此深拷贝的内存开销会比较大。

赋值和深/浅拷贝的区别

当我们把一个对象赋值给一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是说两个对象指向的是同一个存储空间,无论哪个对象发生改变,都是改变的存储空间的内容,因此,两个对象是联动的。

下面举几个示例代码来说明

对象赋值

let obj1 = { name: '张三', details: { age: 25, address: '上海' } };
let obj2 = obj1;
obj2.details.age = 30;
console.log(obj1.details.age); // 30
console.log(obj2.details.age); // 30

浅拷贝

let obj1 = { name: '张三', details: { age: 25, address: '上海' } };
let obj3 = shallowClone(obj1);
obj3.details.age = 30;

function shallowClone(source) {
    let target = {};
    for (let key in source) {
        if (source.hasOwnProperty(key)) {
            target[key] = source[key];
        }
    }
    return target;
}

console.log(obj1.details.age); // 30
console.log(obj3.details.age); // 30

深拷贝

let obj1 = { name: '张三', details: { age: 25, address: '上海' } };
let obj4 = deepClone(obj1);
obj4.details.age = 30;

function deepClone(obj) {
    if (obj === null) return obj;
    if (typeof obj !== "object") return obj;
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    let cloneObj = new obj.constructor();
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            cloneObj[key] = deepClone(obj[key]);
        }
    }
    return cloneObj;
}

console.log(obj1.details.age); // 25
console.log(obj4.details.age); // 30

上面例子中,obj1 是原始对象,obj2 是赋值操作得到的对象,obj3 是浅拷贝得到的对象,obj4 是深拷贝得到的对象。

我们可以发现 无论是对 obj2 还是 obj3 进行修改,原本关联的对象也都会本影响,而 obj4 才不会影响原本的数据。因此只要是对相互关联的其中一个对象修改,另外一个也会受到影响,而赋值和浅拷贝本质上也是关联。

实现方法

浅拷贝的实现方式

1. Object.assign()

let obj1 = { name: '张三', details: { age: 25, address: '上海' } };
let obj2 = Object.assign({}, obj1);
obj2.details.age = 30;
console.log(obj1.details.age); // 30
console.log(obj2.details.age); // 30

Object.assign()方法可以将一个或多个源对象的属性复制到目标对象上。它执行浅拷贝,即仅复制对象的第一层属性。

2. Lodash 的 _.clone

var _ = require('lodash');
let obj1 = { name: '张三', details: { age: 25, address: '上海' } };
let obj2 = _.clone(obj1);
obj2.details.age = 30;
console.log(obj1.details.age); // 30
console.log(obj2.details.age); // 30

Lodash 是一个流行的 JavaScript 工具库,它提供了_.clone()方法用于浅拷贝对象。_.clone()进行浅拷贝,对于对象中嵌套的引用类型属性,它们仍然共享同一块内存。

3.展开运算符 ...

展开运算符是 ES6 引入的一个特性,提供了一种简洁的方式来执行浅拷贝。

let obj1 = { name: 'Kobe', address: { x: 100, y: 100 } };
let obj2 = { ...obj1 };
obj1.address.x = 200;
obj1.name = 'wade';
console.log(obj2); // { name: 'Kobe', address: { x: 200, y: 100 } }

展开运算符...复制了obj1的属性到obj2。同样,由于address是引用类型,obj1.addressobj2.address共享同一个对象。

4.Array.prototype.concat()

concat() 方法用于合并数组或复制数组。对于浅拷贝,通常用于数组的复制。

let arr = [1, 3, { username: 'kobe' }];
let arr2 = arr.concat();
arr2[2].username = 'wade';
console.log(arr); // [1, 3, { username: 'wade' }]

concat() 复制了数组的第一层元素,修改 arr2[2].username 也会影响 arr[2].username,因为这两个数组共享相同的对象引用。

5. Array.prototype.slice()

slice()方法可以用于创建数组的浅拷贝。

let arr = [1, 3, { username: 'kobe' }];
let arr3 = arr.slice();
arr3[2].username = 'wade';
console.log(arr); // [1, 3, { username: 'wade' }]

深拷贝

1.JSON.parse(JSON.stringify())

是一个常见的深拷贝方法,通过将对象转换为 JSON 字符串,再将字符串解析为对象来实现深拷贝。

let arr = [1, 3, { username: 'kobe' }];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan';
console.log(arr, arr4); // arr: [1, 3, { username: 'kobe' }], arr4: [1, 3, { username: 'duncan' }]

注意JSON.parse(JSON.stringify())实现了深拷贝,但不能处理函数、正则表达式和undefined,这些数据在序列化过程中会丢失或被转换。

2 Lodash 的 _.cloneDeep()

Lodash 的 _.cloneDeep() 方法用于实现深拷贝,适用于更复杂的对象结构。

var _ = require('lodash');
var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] };
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f); // false

_.cloneDeep() 递归地复制对象及其嵌套的属性,确保新旧对象完全独立。

3.jQuery.extend()方法

jQuery 提供的 $.extend() 方法可以用于深拷贝对象。第一个参数为 true 时,即为深拷贝。

var $ = require('jquery');
var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] };
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false

$.extend(true, {}, obj1) 实现了深拷贝,递归地复制对象中的属性。

总结

浅拷贝和深拷贝是 JavaScript 中两种不同的对象复制方法,各有其适用场景和特点。浅拷贝仅复制对象的第一层属性,对于引用类型的属性只是复制了其引用地址,因此修改深层属性会影响原始对象。而深拷贝则通过递归复制整个对象,包括所有嵌套的属性,确保新旧对象之间完全独立。

在本文中,我们通过具体的代码示例展示了如何实现浅拷贝和深拷贝,并介绍了几种常见的实现方法,掌握这些方法可以帮助你在实际开发中更有效地处理对象复制问题,避免数据污染和潜在的错误。

接下来给大家推荐一篇我在前端面试多年的经验文章,希望大家看完以后都可以领取到心仪的offer哦!

文章:《聊聊前端面试那些事儿》

标签:obj1,obj,age,let,details,必须,拿下,拷贝
From: https://blog.csdn.net/2401_84489283/article/details/141021168

相关文章

  • C++中深拷贝与浅拷贝
    C++中深拷贝与浅拷贝常见场景为类的拷贝构造函数与赋值运算符重载中。其主要的区别在于指针成员的拷贝上,如果指针成员变量指向的内容位于动态申请的堆内存上,此时浅拷贝只是拷贝了指针变量,会造成多个指针指向同一块内存,当这些对象被析构时,就会造成多次释放同一块内存的问题,即......
  • C++ 禁用类的拷贝构造函数和赋值运算符
    C++中如果没有显式定义类的构造函数和赋值运算符,编译器会自动生成对应的函数,但是对于一些含有指针成员变量的类,自动生成的成员函数只会进行浅拷贝,会导致动态申请的内存在对象析构的时候doublefree,引起崩溃的问题。因此如果没有必要,通常会禁用该接口,避免用户调用该接口造成问题。......
  • 拿下ITSS资质认证有啥用?官方认可的IT服务管理精英?
    ITSS人员认证(InformationTechnologyServiceandSupport)是由中国电子技术标准化研究院推出的,涵盖了“IT服务工程师”和“IT服务经理”的两种人员资格认证。ITSS资质认证作为颇具权威的专业资格认证,它为IT服务和支持人员提供了一个标准化的职业发展路径。这种认证不仅为个人......
  • 26亿损失....这些SMT工厂安全防静电规则必须要牢记了....
    SMT工厂须熟记的安全防静电规则!安全对于我们非常重要,特别是我们这种SMT加工厂,通常我们所讲的安全是指人身安全。但这里我们须树立一个较为全面的安全常识就是在强调人身安全的同时亦必须注意设备、产品的安全。电气:怎样预防人身触电事故?1、发现有损坏的开关,电线等电气......
  • python joblib.load 发生错误:协议 0 中的持久 ID 必须是 ASCII 字符串 在 GCP 云运行
    总体而言:我尝试使用Cloudbuild和Cloudrun构建BERT模型。我将模型(参数)和元数据(标签)保存在GCPCloudStorage中。但是,我遇到了通过joblib.load()加载metadata.bin文件的错误。我的metadata.bin文件包含UTF-8字符,但joblib.load需要ASCII字符。在......
  • 【Harmony Next】七夕前学会创建开屏动画拿下女同事的芳心
    【HarmonyNext】七夕前学会创建开屏动画拿下女同事的芳心一个优秀的项目需要一个*格够高的动画来开启,下面教你用三步快速实现鸿蒙应用的开屏动画1.创建窗口使用windowStage.createSubWindow("splash_window")创建窗口对窗口进行管理,实现加载开屏动画在UIAbility的生命周期......
  • python-深拷贝和浅拷贝
     浅拷贝list_name=["李琪",["周义杰","毛绍祺"]]data01=list_name[:]#触发浅拷贝:只复制第一层,共享深层数据data01[0]="琪琪"#修改第一层,数据2份,互不影响data01[1][0]="义杰"#修改深层,数据1份,数据互相影响print(list_name)print(data01) 深拷贝 作用:互不......
  • C++ 拷贝构造函数语义与移动构造函数语义
    拷贝构造函数语义与移动构造函数语义一、拷贝构造函数语义1.编译器生成拷贝构造函数的条件2.自定义拷贝构造函数3.深拷贝与浅拷贝4.拷贝构造函数的使用场景5.拷贝赋值运算符6.规则五(RuleofFive)二、移动构造函数语义1.禁止生成移动构造函数2.生成移动构造函数......
  • 最好用的深拷贝方法?分享 1 段优质 JS 代码片段!
    本内容首发于工粽号:程序员大澈,每日分享一段优质代码片段,欢迎关注和投稿!大家好,我是大澈!本文约 900+ 字,整篇阅读约需 1 分钟。今天分享一段优质JS代码片段,轻松实现了对象间的深度克隆。老规矩,先阅读代码片段并思考,再看代码解析再思考,最后评论区留下你的见解!const......
  • 超好玩洛谷小游戏大全,好玩到停不下来(用洛谷的人都必须要知道,程序猿、OIer必备)
    Game啊你颓废了快点这个<tuifei break>{\color{White}\colorbox{Pink}{<tuifeibreak>}}<tuifei b......