深拷贝和浅拷贝的实现方式

75 阅读3分钟

深拷贝和浅拷贝的具体意义就不解释了,在工作之前一直知道深拷贝和浅拷贝,但是仅限知道,在自学过程中没有具体使用过。然后导致在看代码的时候遇见json.parse(json.string(对象名))这样一句我还很纳闷这样对象->字符串->对象颠倒一下有什么意义,还去问了辅导员。为了防止类似情况再次发生,在这里做个深拷贝和浅拷贝的总结,方便以后学习。

赋值(非浅拷贝)

简单的引用赋值

let a = {
    name: 'aa',
    age: '18',
    getName(){
        return this.name
    }
}
let b = a
b.name = 'bb'
console.log(a,b);
//结果为
//{ name: 'bb', age: '18', getName: [Function: getName] } 
//{ name: 'bb', age: '18', getName: [Function: getName] }

浅拷贝的实现方式

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝

如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址

即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址。

1.Object.assign()

let a = {
    name: 'aa',
    age: '18',
    children: {
        sex: '男'
    },
    getName(){
        return this.name
    }
}
let b = Object.assign({},a)
b.name = 'bb'                            // b改变一级属性
b.children.sex = '女'                    // b改变二级属性
console.log(a,b);

结果为:

{
  name: 'aa',                            // 发现a一级属性没有发生改变
  age: '18',
  children: { sex: '女' },               // 发现a二级属性已经发生改变
  getName: [Function: getName]
} 
{
  name: 'bb',
  age: '18',
  children: { sex: '女' },
  getName: [Function: getName]
}

2.es6的展开语法

let a = {
    name: 'aa',
    age: '18',
    children: {
        sex: '男'
    },
    getName(){
        return this.name
    }
}
let b = {...a}
b.name = 'bb'                               // b改变一级属性
b.children.sex = '女'                       // b改变二级属性
console.log(a, b);

结果为:

{
  name: 'aa',                               // a的一级属性没有改变
  age: '18',                 
  children: { sex: '女' },                  // a的二级属性发生的改变
  getName: [Function: getName]
} {
  name: 'bb',
  age: '18',
  children: { sex: '女' },
  getName: [Function: getName]
}

3.数组名.slice()和 数组名.concat()

数组是一种独特的对象

slice方法

let arrA = [1, 2, 3, 4, [1, 2, 3]]
let arrB = arrA.slice()
arrB[0] = 'a'                       // arrB改变一级属性
arrB[4][0] = 'a'                    // arrB改变二级属性
console.log(arrA, arrB);

结果为:

[ 1, 2, 3, 4, [ 'a', 2, 3 ] ]             // arrA一级属性没有被改变,二级属性发生了改变
[ 'a', 2, 3, 4, [ 'a', 2, 3 ] ]

concat方法

let arrA = [1, 2, 3, 4, [1, 2, 3]]
let arrB = arrA.concat()
arrB[0] = 'b'                            // arrB改变一级属性
arrB[4][0] = 'b'                         // arrB改变二级属性
console.log(arrA, arrB);

结果为:

[ 1, 2, 3, 4, [ 'b', 2, 3 ] ]           // arrA一级属性没有改变,二级属性发生了改变
[ 'b', 2, 3, 4, [ 'b', 2, 3 ] ]

深拷贝的实现方式

1,暴力美学,使用递归函数

参考另一篇笔记

2,使用JSON.parse(JSON.stringify(对象))方法

就是我在公司遇见的代码。但是这种方法不能复制原对象里的函数。并且原属性的一级属性和二级属性都不发生改变。

let a = {
    name: 'aa',
    age: '18',
    children: {
        sex: '男'                  
    },
    getName(){
        return this.name
    }
}
let b = JSON.parse(JSON.stringify(a))
b.name = 'bb'                                // b改变一级属性
b.children.sex = '女'                        // b改变二级属性
console.log(a, b);
//结果为
//{
//  name: 'aa',                             // a一级属性没有发生改变
//  age: '18', 
//  children: { sex: '男' },                // a二级属性也没有改变
//  getName: [Function: getName] 
//}
//{ name: 'bb', age: '18', children: { sex: '女' } }  // function函数没有了

但是这种方式存在弊端,会忽略undefinedsymbol函数

const obj = {
    name: 'A',
    name1: undefined,
    name3: function() {},
    name4:  Symbol('A')
}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}

3.使用lodash提供的方法

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

4.使用jQuery提供的方法

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