JavaScript 中什么是浅拷贝?、什么是深拷贝?

数据类型

(可以略过)在步入正文之前,我们得先了解以下,什么是基本数据类型什么是引用数据类型

基本数据类型: Number、String、Boolean、Undefined、Null
栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间

引用数据类型: Object、Array、function、Date、RegExp
堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型,指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针

我对声明变量与赋值的理解:当声明了一个变量时就已经使用了栈内存(此时并没有赋值),那么值是undefined,并且声明变量发生在编译期,而赋值是在执行期

1.基本数据类型
使用=赋值时,基本数据类型执行的是复制操作

解析:声明变量a,并且赋值100(创建一个内存空间),声明变量b,并且赋值a(此时a的数据为100,上面有提到基本数据类型在赋值过程中执行的是复制操作)所有现在把a的100复制出来,给到b那么b(创建一个内存空间)此时两个内存空间没有任何关系,随后又把90赋给了a(90)a的数据已经更改,而b是另一个内存空间,并未被修改。

COPY
1
2
3
4
5
var a = 100;
var b = a;
a=90
console.log(a) // 90
console.log(b) // 100

2.引用数据类型
使用=赋值时,引用数据类型执行的是引用(指向)操作

解析: 声明old时初始化了数据{name:'lete114',age:18}(创建内存空间),随后声明obj将old赋值给obj(此时并不是创建新的内存空间,而是把obj指向到了old的内存空间)所有他们两个是有关系的,当修改了name属性和age属性时,由于obj指向old的内存空间,old发生改变后obj也能获取到改变后的值

COPY
1
2
3
4
5
6
7
8
9
10
11
12
var old = {name:'lete114',age:18}
var obj = old

old.name='Lete乐特'
old.age=20
console.log(old) // {name:'Lete乐特',age:20}
console.log(obj) // {name:'Lete乐特',age:20}

obj.name='Lete乐特真帅'
obj.age=20
console.log(old) // {name:'Lete乐特真帅',age:20}
console.log(obj) // {name:'Lete乐特真帅',age:20}

正文

扯了那么长的大水文,终于到正文了,为什么非要说数据类型呢?这关系到本文的内容,只为更详细的记录每一个知识点,如有错误,请各位大佬在评论区留言

问:什么是拷贝?
把A的数据赋值给B,当B改变内容后,观察A的内容是否改变,如果改变则:浅拷贝|反之深拷贝
可以理解为:基本类型自带深拷贝,引用类型自带浅拷贝

浅拷贝

浅拷贝的几种方法(浅拷贝只能拷贝第一层的内容,无法往下递归)

  1. 通过赋值引用的方式(我也不知道这算不算拷贝,因为已经变成另一个变量调用了,所有我认为是拷贝)
    COPY
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var old = {name:'lete114',age:18}
    var obj = old

    old.name='Lete乐特'
    old.age=20
    console.log(old) // {name:'Lete乐特',age:20}
    console.log(obj) // {name:'Lete乐特',age:20}

    obj.name='Lete乐特真帅'
    obj.age=20
    console.log(old) // {name:'Lete乐特真帅',age:20}
    console.log(obj) // {name:'Lete乐特真帅',age:20}
  2. 通过扩展运算符(es6)
    COPY
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 定义一个对象
    const oldObj = {
    name:'lete114',
    age:18,
    friend:{
    name: '小明',
    age:18
    }
    }
    // es6扩展运算符浅拷贝
    let newObj = {...oldObj}
    // 修改属性值
    newObj.name = '乐特'
    newObj.age = 20
    newObj.friend.name = '小李'
    newObj.friend.age = 20
    console.log(oldObj)
    console.log(newObj)
  3. 通过Object.assign()方法(es5)
    COPY
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 定义一个对象
    const oldObj = {
    name:'lete114',
    age:18,
    friend:{
    name: '小明',
    age:18
    }
    }
    // es5 Object.assign()方法浅拷贝
    let newObj = Object.assign({},oldObj)
    // 修改属性值
    newObj.name = '乐特'
    newObj.age = 20
    newObj.friend.name = '小李'
    newObj.friend.age = 20
    console.log(oldObj)
    console.log(newObj)
  4. 通过for循环重新赋值
    COPY
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 定义一个对象
    const oldObj = {
    name:'lete114',
    age:18,
    friend:{
    name: '小明',
    age:18
    }
    }
    // 定义空对象
    let newObj = {}
    // for循环重新赋值浅拷贝
    for (const key in oldObj) {
    newObj[key] = oldObj[key]
    }

    // 修改属性值
    newObj.name = '乐特'
    newObj.age = 20
    newObj.friend.name = '小李'
    newObj.friend.age = 20
    console.log(oldObj)
    console.log(newObj)

深拷贝

  1. 通过JSON.stringify()和JSON.parse()方法
    COPY
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 定义一个对象
    const oldObj = {
    name: 'lete114',
    age: 18,
    friend: {
    name: '小明',
    age: 18
    }
    }
    // 通过JSON.stringify()和JSON.parse()方法
    function DeepClone(old) {
    let str = JSON.stringify(old) // 将对象转换为字符串了(变为基本类型)
    let obj = JSON.parse(str) // 将字符串解析为对象(变为了引用类型)
    return obj
    }
    let newObj = DeepClone(oldObj)
    // 修改属性值
    newObj.name = '乐特'
    newObj.age = 20
    newObj.friend.name = '小李'
    newObj.friend.age = 20
    console.log(oldObj)
    console.log(newObj)
  2. 深拷贝可以拷贝全部,无限递归
    COPY
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    // 定义一个对象
    const oldObj = {
    name:'lete114',
    age:18,
    friend:{
    name: '小明',
    age:18
    }
    }
    // 定义一个空对象
    let newObj = {}

    DeepClone(oldObj,newObj)
    function DeepClone(oldObj,newObj){
    // 循环获取对象
    for(let key in oldObj){
    // 将旧的对象值赋给value
    let value = oldObj[key]
    // 判断是否属于数组
    //(Array必须在Object之前判断,因为Array的底层继承的是Object,如果Object在前面判断的话,当遇到Array时也会被视为Object)
    if (value instanceof Array){
    newObj[key] = [] // 将array类型赋给新对象
    DeepClone(value,newObj[key]) // 重新调用
    }else if(value instanceof Object){
    newObj[key] = {}
    DeepClone(value,newObj[key])
    }else{
    newObj[key] = value
    }
    }
    }

    newObj.name = '乐特'
    newObj.age = 20
    newObj.friend.name = '小李'
    newObj.friend.age = 20
    console.log(oldObj)
    console.log(newObj)
Authorship: Lete乐特
Article Link: https://blog.imlete.cn/article/JavaScript-ShallowClone-DeepClone.html
Copyright: All posts on this blog are licensed under the CC BY-NC-SA 4.0 license unless otherwise stated. Please cite Lete乐特 's Blog !