• js深拷贝与浅拷贝


     1.浅拷贝概念

    浅拷贝是其属性与拷贝源对象的属性共享相同引用,当你更改源或副本时,也可能(可能说的是只针对引用数据类型)导致其他对象也发生更改。

    特性

    • 会新创建一个对象,即obj===obj2返回fasle;
    • 对象中如果是基本数据类型会单独开辟一份空间存储,如果是引用数据类型则共用一个内存地址。即如果是基本数据类型新对象修改不会造成源对象值改变,但是如果是引用数据类型,修改会造成源对象属性同时被修改

    2.var people1 = people不是浅拷贝

    注意var people1 = people不是浅拷贝。只是对内存地址进行赋值,people1 === people 返回true,这种不是浅拷贝

    1. // 这个种方式不是浅拷贝
    2. var people = {
    3. name: 'allen'
    4. }
    5. // 这里只是对内存地址进行赋值,people1 === people 返回true,这种不是浅拷贝
    6. var people1 = people
    7. people1.name = 'jason'

    3.浅拷贝示例

    浅拷贝:会新创建一个对象,对源对象的属性进行拷贝,使两者共享同一个地址。obj === obj2返回false。比如:var obj2 = Object.assign({}, obj)

    说明:Object.assign()是浅拷贝,会生成一个新的对象浅拷贝对于基本数据类型a,会重新单独开辟一份空间进行存储, 而对于引用数据类型b拷贝的是其内存地址,所以obj2和obj对于拷贝后的b是共用一份内存地址。所以如果修改obj2中b的b1的值,则obj中的b1的值也会改变

    4.浅拷贝的方法(Object.create(),Object.assign(),扩展运算符)

    4.1Object.create()

    创建一个对象,会将源对象放到新对象的原型上(即会将obj对象放到obj2的原型上),返回一个新的对象。

    1. var obj = {a: 1}
    2. var obj2 = Object.create(obj)
    3. obj.a = 2 //修改基本数据类型,源对象也会改变
    4. console.log(obj === obj2); //false
    5. console.log(obj,obj2);

    4.2 Object.assign()

    将对象属性进行合并,并返回新的对象,遇到重名对象会进行覆盖

    1. var obj = {
    2. a: 1,
    3. b: {
    4. b1: 1
    5. }
    6. }
    7. var obj2 = Object.assign({}, obj)
    8. obj.a = 2
    9. obj.b.b1 = 2
    10. console.log(obj === obj2);
    11. console.log(obj, obj2);

    结果:源对象基本数据类型值没有改变,引用数据用的同一个会改变

    4.3扩展运算符

    1. var obj = {
    2. a: 1,
    3. b: {
    4. b1: 1
    5. }
    6. }
    7. var obj2 = {...obj};
    8. obj2.a = 3;
    9. obj2.b.b1 = 2;
    10. console.log(obj === obj2);
    11. console.log(obj, obj2);

     结果:同上

    5.深拷贝概念

    深拷贝是指其属性与其拷贝的源对象的属性不共享相同的引用,当你更改源或副本时,可以确保不会导致其他对象也发生更改

    6.深拷贝的方法

    6.1JSON.parse(JSON.stringify())

    原理:通过改变数据类型的形式进行深拷贝,JSON.stringify()将对象序列化成字符串,而JSON.parse()将字符串转为对象。注意不是JSON.stringify()是两个方法组合使用

    1. var obj = {
    2. a: 1,
    3. b: {
    4. b1: 1
    5. }
    6. }
    7. var obj1 = JSON.parse(JSON.stringify(obj))
    8. obj1.a = 2
    9. obj1.b.b1 = 2
    10. console.log(obj === obj2);
    11. console.log(obj, obj2);

    结果:基本数据类型和引用数据类型的改变都不会印象源对象 

    6.2 Lodash.cloneDeep()

    cdn地址 : https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js

    1. import _ form "lodash";
    2. var obj = {
    3. a: 1,
    4. b: {
    5. b1: 1
    6. }
    7. }
    8. var obj1 = _.cloneDeep(obj)
    9. obj1.a = 2
    10. obj1.b.b1 = 2

    7.深拷贝的原理

    7.1主要考虑以下三个方面:

    1. 数据类型的划分
    2. 递归处理
    3. 循环引入的处理:循环引入即对象可以一直展开,无限嵌套,无限循环

    7.1循环引入:

    1. var obj = {};
    2. obj.a = obj;
    3. console.log(obj);

    对象可以一直展开,无限嵌套,无限循环

     8.手动实现深拷贝

     8.1方式一:简单实现(只判断数据基本类型和针对对象数据结构)

    • 判断数据类型,特殊类型直接返回;
    • 遍历对象进行递归处理; 
    1. function deepClone(obj) {
    2. console.log(typeof obj);
    3. // 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回
    4. if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {
    5. return obj;
    6. }
    7. if (obj instanceof Object) {
    8. const newObj = {};
    9. for (let key in obj) {
    10. newObj[key] = deepClone(obj[key]);
    11. }
    12. // 注意是最后return不是在for循环中returnnewObj[key]
    13. return newObj;
    14. }
    15. }
    16. var obj = {
    17. a: 1,
    18. b: {
    19. b1: 1
    20. }
    21. }
    22. var obj2 = deepClone(obj);
    23. obj2.a = 2;
    24. obj2.b.b1 = 2
    25. console.log(obj === obj2);
    26. console.log(obj, obj2);

     

    8.2 方式二(增强): 针对Map、Set、Array等结构进行深拷贝处理

    •  判断数据类型,特殊类型直接返回;
    • 遍历对象进行递归处理; 
    • 对Map,Set,Array数据结构进行处理
    • 注意Map,Set,Array各自不同遍历及修改的方法
    1. function deepClone(obj) {
    2. // 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回
    3. if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {
    4. return obj;
    5. }
    6. if (obj instanceof Set) { //对于Set数据结构(for of 循环;newSet.add(val))
    7. const newObj = new Set();
    8. for (let val of obj) {
    9. newObj.add(deepClone(val));
    10. }
    11. return newObj;
    12. } else if (obj instanceof Map) { //对于Map数据结构(for of循环 map.set(key,val))
    13. const newObj = new Map();
    14. for (let [key, val] of obj) {
    15. newObj.set(key, deepClone(val));
    16. }
    17. return newObj;
    18. } else if (obj instanceof Array) { //对于数组数据结构(使用forEach进行循环push进行添加)
    19. const newObj = [];
    20. obj.forEach(item => {
    21. newObj.push(deepClone(newObj[item]));
    22. });
    23. return newObj
    24. } else if (obj instanceof Object) {
    25. const newObj = {};
    26. for (let key in obj) {
    27. newObj[key] = deepClone(obj[key]);
    28. }
    29. // 注意是最后return不是在for循环中returnnewObj[key]
    30. return newObj;
    31. } else {
    32. return obj;
    33. }
    34. }
    35. var newMap = new Map();
    36. newMap.set('key1', 12);
    37. newMap.set('key2', 13);
    38. var newSet = new Set();
    39. newSet.add('11');
    40. newSet.add('22');
    41. var obj = {
    42. a: 1,
    43. b: {
    44. b1: 1
    45. },
    46. c: {
    47. c1: [1, 2, 3]
    48. },
    49. d: newMap,
    50. f: newSet
    51. }
    52. var obj2 = deepClone(obj);
    53. obj2.a = 2;
    54. // 基本数据类型修改值
    55. obj2.b.b1 = 2
    56. // 数组改变值
    57. obj2.c.c1 = [2, 3, 4];
    58. // map集合修改key1
    59. obj2.d.set('key1','更改后的key1');
    60. // Set集合增加一个
    61. obj2.f.add('set集合增加的值');
    62. console.log(obj === obj2);
    63. console.log(obj, obj2);

    8.3 方式三(循环引用的处理)

    •  判断数据类型,特殊类型直接返回;
    • 遍历对象进行递归处理; 
    • 对Map,Set,Array数据结构进行处理
    • 注意Map,Set,Array各自不同遍历及修改的方法

    如果有循环引用,使用方式二深拷贝处理,会一直循环导致内存溢出。map = new WeakMap()用于判断是否已经有过相同引用

    1. function deepClone(obj, map = new WeakMap()) {
    2. // 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回
    3. if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {
    4. return obj;
    5. }
    6. // 循环引入处理(如果obj下的key的值为obj本身,则循环将obj的值设置到map的key中)
    7. // WeakMap的key只能是对象,Set、Map、Array、Object每个都设置map.set(obj,newObj);是因为每种数据结构都有可能存在循环引用
    8. if (map.get(obj)) {
    9. return map.get(obj);
    10. }
    11. if (obj instanceof Set) { //对于Set数据结构(for of 循环;newSet.add(val))
    12. const newObj = new Set();
    13. map.set(obj, newObj);
    14. for (let val of obj) {
    15. newObj.add(deepClone(val, map));
    16. }
    17. return newObj;
    18. } else if (obj instanceof Map) { //对于Map数据结构(for of循环 map.set(key,val))
    19. const newObj = new Map();
    20. map.set(obj, newObj);
    21. for (let [key, val] of obj) {
    22. newObj.set(key, deepClone(val, map));
    23. }
    24. return newObj;
    25. } else if (obj instanceof Array) { //对于数组数据结构(使用forEach进行循环push进行添加)
    26. const newObj = [];
    27. map.set(obj, newObj);
    28. obj.forEach(item => {
    29. newObj.push(deepClone(newObj[item], map));
    30. });
    31. return newObj
    32. } else if (obj instanceof Object) {
    33. const newObj = {};
    34. map.set(obj, newObj); //此处设置后,后面newObj即使更改,map内存地址使用的是一个所以map的值也会跟着变
    35. for (let key in obj) {
    36. newObj[key] = deepClone(obj[key], map);
    37. }
    38. // 注意是最后return不是在for循环中returnnewObj[key]
    39. return newObj;
    40. } else {
    41. return obj;
    42. }
    43. }
    44. // 循环引入
    45. var obj3 = {};
    46. obj3.a = obj3;
    47. var obj4 = deepClone(obj3);
    48. obj4.b = obj4;
    49. console.log(obj3 === obj4);
    50. console.log(obj3, obj4);

  • 相关阅读:
    《CTF特训营》学习笔记——APK逆向基础
    生成式模型和判别式模型
    MATLAB - 不能使用PYTHON,缺少matplotlib模块的解决办法
    《机器学习分类器 二》——朴素的贝叶斯算法,项目实践,算法实践。
    计算机网络学习三(以太网基本概念)
    具有CT造影功能的硫化铋纳米棒|硫化铋-锌原卟啉复合材料(PAMAM/Bi2S3复合纳米粒子)
    Mysql 45讲学习笔记(二十九)判断数据库是否可用
    GitHub上的开源工业软件
    选择远程办公,选择放弃远程办公
    第六章:最新版零基础学习 PYTHON 教程—Python 正则表达式(第一节 - Python 正则表达式)
  • 原文地址:https://blog.csdn.net/qq_34569497/article/details/133815688