JavaScript中浅拷贝和深拷贝的区别与实现

本文详细介绍了JavaScript中的数据类型,特别是基础数据类型和引用数据类型之间的区别。重点讨论了深拷贝和浅拷贝的概念,其中浅拷贝仅复制对象的引用,而深拷贝会创建全新的对象副本。文中列举了多种实现深拷贝和浅拷贝的方法,包括使用`Object.assign()`、JSON对象、jQuery的`extend`、lodash的`cloneDeep`以及扩展运算符等,并指出各种方法在处理复杂对象时的局限性和适用场景。此外,还给出了实例代码展示如何进行深拷贝和浅拷贝操作,帮助读者理解这两种拷贝方式的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaScript有两种数据类型,基础数据类型和引用数据类型。基础数据类型都是按值访问的,我们可以直接操作保存在变量中的实际的值。而引用类型如Array,我们不能直接操作对象的堆内存空间。引用类型的值都是按引用访问的,即保存在变量对象中的是一个地址,该地址与堆内存的实际值相关联。
参考:JavaScript 基础数据类型和引用数据类型

1、深拷贝和浅拷贝的区别

  • 浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存。
  • 深拷贝(deep copy):复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。

2、浅拷贝的实现

浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用(地址),所以改变新对象,旧对象也会改变,因为新旧对象共享一块内存。

通过 Object.assign() 拷贝

Object.assign(目标文件, 原文件) 浅拷贝的方法

注意:

  • 当对象只有一级属性为深拷贝;
  • 当对象中有多级属性时,二级属性后就是浅拷贝;
    <script>
        // 浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用(地址)
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            }
        };
        var o = {};
        // for (var k in obj) {
        //     // k 是属性名   obj[k] 属性值
        //     o[k] = obj[k];
        // }
        // console.log(o);
        // o.msg.age = 20;
        // console.log(obj);

        console.log('--------------');
        // Object.assign(目标文件, 原文件)  浅拷贝的方法
        Object.assign(o, obj);
        console.log(o);
        o.msg.age = 20;
        console.log(obj);
        // 结果就是 o 和 obj 的 msg.age 都变成了20, 因为 o 和 obj 是共享一块内存的
    </script>

3、深拷贝的实现

深拷贝的原理:复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。

3.1 递归遍历所有层级,实现深拷贝

我们采用递归的方式来封装实现深拷贝的函数。

// 1. 使用递归的方式实现深拷贝
// 深拷贝拷贝多层, 每一级别的数据都会拷贝.
var obj = {
    id: 1,
    name: 'andy',
    msg: {
        age: 18
    },
    color: ['pink', 'red']
};

var o = {};
// 封装函数
function deepCopy(newobj, oldobj) {
    for (var k in oldobj) {
        // 判断我们的属性值属于那种数据类型
        // 1. 获取属性值  oldobj[k]
        var item = oldobj[k];
        // 2. 判断这个值是否是数组(首先判断数组是因为数组也属于对象)
        if (item instanceof Array) {
            newobj[k] = [];
            deepCopy(newobj[k], item);
        } else if (item instanceof Object) {
            // 3. 判断这个值是否是对象
            newobj[k] = {};
            deepCopy(newobj[k], item);
        } else {
            // 4. 属于简单数据类型
            newobj[k] = item;
        }
    }
};
deepCopy(o, obj);
o.msg.age = 20;
console.log(o);
console.log(obj);   // obj.msg.age 还是等于 18 没有改变
var arr = [];
console.log(arr instanceof Object);  // true 数组也是对象

3.2 利用 JSON 对象实现深拷贝

// 深拷贝拷贝多层, 每一级别的数据都会拷贝.
var obj = {
    id: 1,
    name: 'andy',
    msg: {
        age: 18
    },
    color: ['pink', 'red']
};

// 2. 利用 JSON 对象
function deepCopy(obj) {
    let _obj = JSON.stringify(obj);
    let newObj = JSON.parse(_obj);
    return newObj;
}

let newObj = deepCopy(obj);
newObj.msg.age = 22;
console.log(newObj);  //  newObj.msg.age = 22
console.log(obj);  // obj.msg.age 还是等于 18 没有改变

注意: 无法实现对象中方法的深拷贝。

3.3 通过 jQuery 的 extend 方法实现深拷贝

jQuery 的 extend 方法也可以拷贝对象。

$.extend([deep ], target, object1 [, objectN ])
  • deep:表示是否深拷贝,true,为深拷贝;false,为浅拷贝。
  • target:Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
  • object1: objectN 可选。 Object 类型 第一个以及第 N 个被合并的对象。
let a = [0,1,[2,3],4]
let b = $.extend(true, [], a)
a[0] = 1
a[2][0] = 1  // [1,1,[1,3],4]
b  // [0,1,[2,3],4]

3.4 Object.assign(),slice,concat 拷贝

当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。

(1)通过 Object.assign() 拷贝

Object.assign(目标文件, 原文件) 浅拷贝的方法

var obj = {a:1,b:2}
var newObj = Object.assign({},obj)

(2)使用 concat 实现对数组的深拷贝

concat(arr1, arr2,....)

var arr = [1,2,3]
var newArr = [].concat(arr)

(3)使用 slice 实现对数组的深拷贝

slice(idx1, idx2)
var arr = [1,2,3]
var newArr = arr.slice(0)
// slice 接受两个参数,第一个表示起始索引,第二个表示结束位置索引,省略时表示一直到末尾

参数可以省略

1)没有参数是拷贝数组

2)只有一个参数是从该位置起到结束拷贝数组元素

3)两个参数,拷贝从起始位置到结束位置的元素(不包含结束位置的元素:含头不含尾)

注意:

  • 当数组中的元素均为一维是深拷贝。

  • 数组中元素一维以上是值的引用。

3.5 lodash函数库实现深拷贝

lodash 很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝。

_.cloneDeep(value)

value : 要深拷贝的值。
返回拷贝后的值

vue 中使用 :
  	 a. npm i --save lodash     下载依赖
  	 b. import _ from 'lodash'  在 组件 中引入 
  	 c. 用法和下面的一样

var objects = [{ 'a': 1 }, { 'b': 2 }]; 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

参考:https://www.lodashjs.com/docs/lodash.cloneDeep

3.6 使用扩展运算符实现数组深拷贝

var a=[1,2,3]
var b=[...a];
b.push(4);
console.log(b);//1,2,3,4
console.log(a)//1,2,3
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值