• javascript的深浅拷贝


    注意:拷贝只针对 Object 和 Array 等引用数据类型,而 赋值 并不是一种拷贝操作。

    赋值

    把一个对象 赋值 给一个新的变量时,只是将栈空间中存储的对象地址复制了一份给新的变量,新的变量和对象依旧指向同一个堆中的存储空间。

    1. 把数组的内存地址赋值给newArr,这里不叫拷贝,修改时互相影响。
    2. let arr = [1,2,3]
    3. let newArr = arr

     浅拷贝

    把对象或数组,进行 浅拷贝 操作时,会在堆空间中生成一个新的对象,这个对象会精准复制原对象的所有属性值,并将新的变量指向这个堆中新产生的对象。

    1. 使用扩展运算符实现浅拷贝,还可以通过Object.assign(), Array.prototype.slice(), Array.prototype.concat()等方法实现对象和数组的浅拷贝。
    2. let obj = {
    3. name: 'Jerry'
    4. }
    5. let arr = [1,2,3]
    6. Object中的assign方法 将对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象
    7. let obj_1 = Object.assign({},obj)
    8. 展开运算符实现浅拷贝
    9. let arr_1 = [...arr]
    10. let obj_1 = {...obj}
    11. Array.prototype.concat() 用于合并两个或多个数组,并返回新的数组
    12. let arr_2 = arr.concat([])
    13. Array.prototype.slice() 回一个新的数组对象,这一对象是一个由 start 和 end 决定原数组的浅拷贝
    14. let arr_3 = arr.slice()

    浅拷贝缺点

    针对基础类型 对象 或者 数组 浅拷贝复制出的新数据是完全独立于旧数据。

    针对复杂类型 对象 的属性和 数组 的元素也是引用数据类型时,浅拷贝 仍然只会复制该引用数据类型堆中的地址。

    1. let arr = [1,2,[3]]
    2. let obj = {
    3. name: 'Jerry'
    4. info: {
    5. age: 18,
    6. height: 180
    7. }
    8. }
    9. 展开运算符实现浅拷贝
    10. let arr_1 = [...arr]
    11. let obj_1 = {...obj}
    12. 修改新对象中的属性值
    13. arr_1[2] = [3,4]
    14. obj_1.info = 'null'
    15. 打印原对象数据 说明:复杂类型 新对象和原对象共享堆中的存储地址
    16. console.log(arr[2]) [3,4]
    17. console.log(obj.info) 'null'

    深拷贝

    深拷贝 在构造新的数据时,遇到引用所指向的引用数据类型会继续执行拷贝,直到所有的引用数据类型都被处理。

    1. JSON.stringify()将一个 JavaScript 对象或值转换为 JSON 字符串, 而 JSON.parse() 则正好相反,它将JSON字符串解析为值或对象,我们以JSON字符串作为桥梁,来实现 深拷贝。
    2. let obj = {
    3. name: 'Jerry'
    4. info: {
    5. age: 18
    6. height: 180
    7. }
    8. }
    9. let obj_1 = JSON.parse(JSON.stringify(obj))

    JSON.parse(JSON.stringify(obj))缺点

    如果obj里面存在时间对象,JSON.parse(JSON.stringify(obj))之后,时间对象变成 字符串

    如果obj里有RegExpError对象,则序列化的结果将只得到 空对象

    如果obj里有函数undefined,则序列化的结果会把函数, undefined丢失

    如果obj里有NaNInfinity-Infinity,则序列化的结果会变成 null

    如果obj中的对象是由构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的 constructor

    如果对象中存在循环引用的情况也无法正确实现 深拷贝

    手写深拷贝

    1. function deepClone(obj, cache = new WeakMap()) { // weakmap做缓存处理循环引用,且不影响垃圾回收
    2. if (typeof obj === 'symbol') return Symbol(obj.description) // 处理symbol
    3. if (obj === null || typeof obj !== 'object') return obj // 对于空对象,函数或者基本数据类型直接返回
    4. if (cache.get(obj)) return cache.get(obj) // 如果缓存中有该对象,说明有循环引用,直接返回该对象,不重复拷贝
    5. // 当为以下类型时直接new一个新对象
    6. const type = [Date, RegExp, Set, Map, WeakMap, WeakSet]
    7. if (type.includes(obj.constructor)) return new obj.constructor(obj)
    8. let cloneObj = new obj.constructor() // 让拷贝对象拥有原对象的构造函数类型
    9. cache.set(obj,cloneObj) // 拷贝对象写入缓存
    10. for(let key in obj) {
    11. if(obj.hasOwnProperty(key)){ // 如果该属性在对象上而不是其原型上
    12. cloneObj[key] = deepClone(obj[key],cache) // 递归调用
    13. }
    14. }
    15. return cloneObj
    16. }

  • 相关阅读:
    【C++】日期类的实现
    数字金融面板数据:金融效率、数字金融指数、不变价GDP、经济开放度
    一个99%的人都说不清楚知识点——Spring 事务传播行为
    Tensorflow源码编译
    【01】Java代码如何运行
    深入了解 Spring 篇之 BeanDefinition 结构
    使用神经网络实现对天气的预测
    XXL-Job和Elastic-job的区别
    wordpress网站制作教程
    MySQL存储过程的基本用法
  • 原文地址:https://blog.csdn.net/qq_46344419/article/details/133301372