浅拷贝、深拷贝的区别

在说浅拷贝深拷贝之前,我们在说下赋值

  1. 赋值

赋值是将某一数值或某对象赋给某个变量的过程,分为下面两部分

  • 基本数据类型:赋值之后两个变量互不影响

  let a = 10; 
  let b = a;
  a = 20;
  console.log('a', a); //20
  console.log('b', b); //10
  • 引用数据类型:两个变量具有相同的引用,指向同一个对象,相互之间有影响

    let a = { 
        name: '桀桀桀'
    }
    let b = a
    a.name = '哈哈哈' // 对引用类型进行赋值操作,两个变量指向相同的对象,a里面的数据改变,b也会该改变
    console.log('obj', a); //哈哈哈
    console.log('src', b); //哈哈哈

2.浅拷贝和深拷贝的区别

浅拷贝和深拷贝都是对一份数据进行拷贝;

js分为基本数据类型和引用数据类,对于基本数据类型并没有深浅拷贝的区别,我们所有的深浅拷贝的区别只是针对引用类型。

  • 浅拷贝和深拷贝都复制了值和地址,都是为了解决引用类型赋值后的互相影响的问题;

  • 浅拷贝只进行了一层赋值 ,深层次的引用类型还是共享内存地址,源对象和拷贝对象还是会互相影响

3.浅拷贝

3.1、什么是浅拷贝

创建一个对象,把这个对象拷贝一下,如果属性是基本数据类型,拷贝的就是基本数据类型的值,如果属性是引用类型,拷贝的就是内存地址,所以就是如果其中一个对象进行改变了这个地址,就会影响到另一个对象。

如上图,浅拷贝只是拷贝了第一次的基本数据类型以及第一层的引用类型,浅拷贝可以说成只是赋值,两个对象的内存地址还是一样的。

3.2、浅拷贝的实现
let obj = {
            name: '小明',
            age: 2,
            friend: {
                name: '小刚'
            }
        }
        //   浅拷贝
    function copy(obj) {
        let newObj = {}
        for (let i in obj) {
            newObj[i] = obj[i]
        }
        return newObj
    }
    let newObject = copy(obj)
    obj.name = '小红'; //改变原来对象
    console.log('原来对象', obj); //小红
    console.log('新的对象', newObject) //小明   
    console.log('两者指向同一地址', obj == newObject);//false

浅拷贝只是解决了一层的数据处理,更深层的对象还是会指向同一地址,互相影响;

3.3、浅拷贝的使用
  • 数组的slice和concat方法

 // slice 方法返回一个新的数组对象
    let arr = ['cat', 'dog', 'big']
    let newArr = arr.slice(0);
    arr[2] = 'tiger'; //改变原数组
    console.log('原数组', arr); //'cat', 'dog', 'tiger'
    console.log('新数组', newArr); //'cat', 'dog', 'big'
    console.log('两者指向同一地址', arr == newArr); //false
    //concat方法用于连接两个或多个数组,该方法不会改变现有的数组,而是返回一个新的数组。
    let arr = ['cat', 'dog', 'big']
    let newArr = [].concat(arr)
    arr[2] = 'tiger'; //改变原数组
    console.log('原数组', arr); //'cat', 'dog', 'tiger'
    console.log('新数组', newArr); //'cat', 'dog', 'big'
    console.log('两者指向同一地址', arr == newArr); //false
  • 扩展运算符

    let arr = ['cat', 'dog', 'big']
    let newArr = [...arr]
    arr[2] = 'tiger'; //改变原数组
    console.log('原数组', arr); //'cat', 'dog', 'tiger'
    console.log('新数组', newArr); //'cat', 'dog', 'big'
    console.log('两者指向同一地址', arr == newArr); //false
  • 数组静态语法Array.from

    let arr = ['cat', 'dog', 'big']
    let newArr = Array.from(arr)
    arr[2] = 'tiger'; //改变原数组
    console.log('原数组', arr); //'cat', 'dog', 'tiger'
    console.log('新数组', newArr); //'cat', 'dog', 'big'
    console.log('两者指向同一地址', arr == newArr); //false
  • Object.assign()

   let obj = {
        name: '小朱'
    }
    let newObj = Object.assign({}, obj)
    obj.name = '小马'; //改变原来的对象
    console.log('原数组', obj); //小马
    console.log('新数组', newObj); //小朱
    console.log('两者指向同一地址', obj == newObj); //false

4.深拷贝

4.1什么是深拷贝

深拷贝会拷贝所有的属性,在内存中开辟一个新的存储空间,完完整整的拷贝一个新的对象(数组),和原对象(数组)完全分隔,彼此之间的操作互相不影响。

4.2深拷贝的实现

下面的例子我使用了递归来实现了一个深拷贝,让原对象和克隆对象互不影响

  let obj = {
        name: '小明',
        age: 2,
        friend: {
            name: '小刚'
        }
    }

    function copy(obj) {
        let newObj = {};
        for (let i in obj) {
            if (obj[i] instanceof Object) { //如果是引用类型,递归实现每层的拷贝
                newObj[i] = copy(obj[i])    //递归拷贝每一次
            } else {
                newObj[i] = obj[i]  //否则就无需拷贝
            }
        }
        return newObj
    }

    let newObject = copy(obj);
    obj.friend.name = '小李';
    console.log('原对象', obj); // obj.friend.name是小李
    console.log('新对象', newObject) // obj.friend.name小刚
    console.log('两者指向同一地址', obj.friend == newObject.friend); //false
4.3深拷贝的使用
  • 处理数组、日期、正则、null

上文中只是用了一个简单的列子讲解了深拷贝,但没有处理null这种原始类型,也没处理数组、日期和正则这种比较常用的引用类型。

 let obj = {
        arr: ['cat', 'dog', 'big'],
        time: new Date(),
        regular: /abc/,
        type: null,
    }

    function copy(obj) {
        // console.log(obj);
        if (obj === null) return obj //处理null
        if (obj instanceof Date) return new Date(obj) // 处理日期
        if (obj instanceof RegExp) return new RegExp(obj) // 处理正则
        let newObj = {};
        for (let i in obj) {
            if (obj[i] instanceof Object) { //如果是引用类型,递归实现每层的拷贝
                newObj[i] = copy(obj[i]) //这个地方我使用了递归方法
            } else {
                newObj[i] = obj[i]
            }
        }
        return newObj
    }
    let newObject = copy(obj);
    console.log('原对象', obj); // 
    console.log('新对象', newObject) //

上面的代码也可以这样实现,如下:

使用new 实例.constructor()

 let obj = {
        arr: ['cat', 'dog', 'big'],
        time: new Date(),
        regular: /abc/,
        type: null,
    }

    function copy(obj) {
        // console.log(obj);
        if (obj === null) return obj //处理null
        if (obj instanceof Date) return new Date(obj) // 处理日期
        if (obj instanceof RegExp) return new RegExp(obj) // 处理正则
        if (typeof obj !== 'object') return obj // 处理原始类型
        let newObj = new obj.constructor(); //new实例
        for (let i in obj) {
            newObj[i] = copy(obj[i])
                // if (obj[i] instanceof Object) { //如果是引用类型,递归实现每层的拷贝
                //     newObj[i] = copy(obj[i]) //这个地方我使用了递归方法
                // } else {
                //     newObj[i] = obj[i]
                // }
        }
        return newObj
    }
    let newObject = copy(obj);
    console.log('原对象', obj); // 
    console.log('新对象', newObject) //

实例的 constructor 其实就是构造函数

 class Person {}
    let obj = new Person();
    console.log(obj.constructor === Person); //true
    console.log([].constructor === Array); //true
    console.log({}.constructor === Object); //true
    // ----------------------------------------
    console.log(new [].constructor()); //[]
    // ||
    console.log(new Array()); //[]
    // -----------------------------------------
    console.log(new {}.constructor()) // {}
    // ||
    console.log(new Object()) // {}

使用 constructor就不用在拷贝时去判断数组类型了,原对象是对象时,就拷贝一个新对象,原对象是数组时,就拷贝一个新数组;

5.总结

关于浅拷贝和深拷贝的使用选择,保险的做法是所有的拷贝都用深拷贝,如果JSON.parse(JSON.stringify(object))可以实现的话,就可以直接JSON.parse(JSON.stringify(object)),还方便。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值