前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >再说深拷贝对象的研坑

再说深拷贝对象的研坑

作者头像
张炳
发布于 2020-05-11 09:31:23
发布于 2020-05-11 09:31:23
54400
代码可运行
举报
运行总次数:0
代码可运行

浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。 深拷贝:会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

浅拷贝的实现方式

直接赋值一个变量

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  let obj = {username: 'kobe', age: 39, sex: {option1: '男', option2: '女'}};
  let obj1 = obj;
  obj1.sex.option1 = '不男不女'; // 修改复制的对象会影响原对象
  console.log(obj1, obj);

Object.assign()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let obj = {
    username: 'kobe'
    };
let obj2 = Object.assign(obj);
obj.username = 'wade';
console.log(obj2);//{username: "wade"}

Array.prototype.concat()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let arr = [1, 3, {
    username: 'kobe'
    }];
let arr2=arr.concat();    
arr2[2].username = 'wade';
console.log(arr);

修改新对象会改到原对象:

Array.prototype.slice()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

同样修改新对象会改到原对象:

关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。详细规则请看MDN对应函数讲解。

深拷贝的实现方式

只有值类型数据的深拷贝

针对只有值的数据对象,下面一行代码足以!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
JSON.parse(JSON.stringify(obj))

原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。这种方法只能处理只有值类型数据的拷贝。

不严谨的简单的深拷贝

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function clone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
             //通过hasOwnProperty方法来进行筛选,所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
            if (typeof source[i] === 'object') {
                target[i] = clone(source[i]); // 注意这里
            } else {
                target[i] = source[i];
            }
        }
    }

    return target;
}

问题存在:

  • 没有对参数做检验
  • 判断是否对象的逻辑不够严谨
  • 没有考虑数组的兼容

进阶深拷贝

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}

function deepCopy(obj)
{
    let tempObj = Array.isArray(obj) ? [] :{};
    for(let key in obj)
    {
        tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key];
    }
    return tempObj;
}

问题存在:

  • __拷贝环__,也就是对 对象循环引用 的拷贝出现问题

对象环知识点补充

方法一 用try catch的捕获异常的方法来判断,代码简洁

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function cycleDetector (obj) {
    console.log(arguments)
  // 请添加代码
    let result = false;
    try {
        JSON.stringify(obj);
    } catch (e) {
        result = true;
    } finally {
        return result;
    }
}

方法二 时间更快,但是它执行递归,逻辑较第一种更复杂,空间也需要更大

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function cycleDetector2(obj) {
    let hasCircle = false,
        cache = [];

    (function(obj) {
        Object.keys(obj).forEach(key => {
            const value = obj[key]
            if (typeof value == 'object' && value !== null) {
                const index = cache.indexOf(value)
                if (index !== -1) {
                    hasCircle = true
                    return
                } else {
                    cache.push(value)
                    arguments.callee(value)
                    // (注:箭头函数没有arguments对象,此时的arguments指向该匿名函数的参数)
                }
            }
        })
    })(obj)

    return hasCircle
}

针对环的深拷贝

可以使用一个WeakMap结构存储已经被拷贝的对象,每一次进行拷贝的时候就先向WeakMap查询该对象是否已经被拷贝,如果已经被拷贝则取出该对象并返回,将deepCopy函数改造成如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj, hash = new WeakMap()) {    
    if(hash.has(obj)) return hash.get(obj)    
    let cloneObj = Array.isArray(obj) ? [] : {}
    hash.set(obj, cloneObj)    
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }   
    return cloneObj
}

问题存在:

  • 没有考虑对new Date(),正则,函数类型的对象的拷贝

结合环,针对date,reg,箭头函数类型的深拷贝

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const obj = {    arr: [111, 222],    obj: {key: '对象'},    a: () => {console.log('函数')},    date: new Date(),    reg: /正则/ig}

function isObj(obj)
{
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj, hash = new WeakMap()) {  
      let cloneObj;   
      let Constructor = obj.constructor; 
      switch(Constructor){       
        case RegExp:
            cloneObj = new Constructor(obj)         
            break;
        case Date:
            cloneObj = new Constructor(obj.getTime())           
            break;
        case Function:
            cloneObj = eval(obj.toString());
            break;
        default:           
         if(hash.has(obj)) return hash.get(obj)
            cloneObj = new Constructor()
            hash.set(obj, cloneObj)
      }   

      for (let key in obj) {
            cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
      }    
      return cloneObj;
}

const cloneObj = deepCopy(obj);
console.log(cloneObj);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【JS专栏】JS对象的浅拷贝与深拷贝
自己创建一个新的对象,来接受你要重新复制或引用的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址,肯定会影响到另一个对象。
Vam的金豆之路
2021/12/01
2.4K0
【JS专栏】JS对象的浅拷贝与深拷贝
深拷贝浅拷贝和赋值的差异
在工作中我们经常会用到深拷贝与浅拷贝,但是你有没有去分析什么场景下使用它,为什么需要使用呢,深浅拷贝有何异同呢,什么是深拷贝呢,如何实现呢,你会有这些问题吗,今天就为大家总结一下吧。
Snine
2022/02/11
4860
JS浅拷贝与深拷贝
本来只想改变obj2的 value 的值,但是改变之后连obj1的值也一同改变了,很显然,这不是我们想要的的结果。
九旬
2020/10/23
1.7K0
js浅拷贝与深拷贝
浅拷贝 只拷贝了基本类型数据和引用类型数据的指针,叫浅拷贝 被拷贝的对象里,如果没有引用类型的数据,可以使用浅拷贝,方便快捷。 如果有引用类型,那么存在被篡改的风险,更应该使用深拷贝 浅拷贝方法 1、手撸一个浅拷贝函数 1function shallowClone(obj) { 2 const newObj = {}; 3 for(let prop in obj) { 4 if(obj.hasOwnProperty(prop)){ 5 newObj[p
心念
2023/01/11
1.2K0
高频js手写题之实现数组扁平化、深拷贝、总线模式
古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。看懂一道算法题很快,但我们必须将这道题的思路理清、手写出来。
helloworld1024
2022/10/06
3900
赋值、浅拷贝、深拷贝的区别
基本类型数据保存在在栈内存中 引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中。
木子星兮
2020/07/16
1.2K0
JS拷贝指南:浅拷贝与深拷贝详解
在JavaScript编程中,数据的复制是一个基础而又至关重要的概念,尤其在处理复杂的数据结构时,正确地执行拷贝操作可以避免意料之外的数据修改问题。JavaScript中的数据类型分为基本类型(如number、string、boolean等)和引用类型(如object、array、function等)。基本类型的值存储的是值本身,而引用类型存储的是指向该数据在内存中位置的引用。因此,对于引用类型而言,拷贝操作分为浅拷贝(Shallow Copy)和深拷贝(Deep Copy)两种情况。 浅拷贝:表面的复制 浅拷贝创建一个新的对象或数组,但它仅复制第一层的元素或属性,如果这些元素或属性是引用类型,则新旧对象将共享同一份引用。这意味着对拷贝后对象中引用类型属性的修改会影响到原对象。以下是一些实现浅拷贝的方法:
FGGIT
2024/11/19
6020
完美解决JavaScript的深浅拷贝
"拷贝"一直都是面试的热门考题。看似简单,实则难住不少面试者,回答的马马虎虎,模棱两可。抽出时间好好分析总结一下"拷贝",让这个难题彻底消失。
小丑同学
2020/09/20
5140
一文搞懂JS中的赋值·浅拷贝·深拷贝
为什么写拷贝这篇文章?同事有一天提到了拷贝,他说赋值就是一种浅拷贝方式,另一个同事说赋值和浅拷贝并不相同。我也有些疑惑,于是我去MDN搜一下拷贝相关内容,发现并没有关于拷贝的实质概念,没有办法只能通过实践了,同时去看一些前辈们的文章总结了这篇关于拷贝的内容,本文也属于公众号【程序员成长指北】学习路线中【JS必知必会】内容。
coder_koala
2019/07/30
3.2K0
一文搞懂JS中的赋值·浅拷贝·深拷贝
大全!JavaScript中深浅拷贝内部方法与手写函数
怎么理解这句话:浅拷贝过程实质上是创建了一个新的变量,但这个新变量与原变量指向同一个内存地址上的对象。这意味着原对象和拷贝对象共享相同的数据结构和内部状态。因此,对拷贝对象所做的任何修改,如果涉及到修改共享的数据结构,也会影响到原始对象。同样的,原始对象所做的任何修改,如果涉及到修改共享的数据结构,也会影响到拷贝对象。
用户6256742
2024/08/13
2020
【面试题解】JavaScript的深浅拷贝,如何手写深拷贝?
本系列面试题旨在学会相关知识点,从而轻松应对面试题的各种形式,本文讲解了 JavaScript 中拷贝的相关知识,以及如何手写深浅拷贝。
一尾流莺
2022/12/10
4750
【面试题解】JavaScript的深浅拷贝,如何手写深拷贝?
更简洁的深拷贝实现思路
深度克隆(深拷贝)一直都是初、中级前端面试中经常被问到的题目,网上介绍的实现方式也都各有千秋,大体可以概括为三种方式:
前端达人
2022/04/18
6640
更简洁的深拷贝实现思路
分享 4 种 JS 深拷贝的方法
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的是内存地址 。
前端达人
2022/06/09
12.3K0
js深拷贝和浅拷贝具体使用区别_es6深拷贝和浅拷贝
对于这个问题,可以考虑从深拷贝和浅拷贝的使用或者起源说起,也就是为什么会出现这个问题。
全栈程序员站长
2022/08/04
6850
js对象和类的操作
深度拷贝最主要需要考虑的因素就是需要考虑js的各种数据类型 1)6种基本类型(number,string,null,undefined,boolean,symbol)不需要处理,直接复制返回即可 2)几种特殊类型 date、regexp、set、map,直接new一遍即可 3)需要着重处理的类型 Array、Object,直接使用递归处理即可 4)递归处理Array、Object的过程中需要考虑循环引用,循环引用处理可以利用WeakMap将每次递归的对象存储起来,如果一样,直接返回
epoos
2022/06/06
2.4K0
js对象和类的操作
js数组浅拷贝_js数组深度复制
浅拷贝:创建一个新的对象,来接受重新复制或引用的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址,会影响到另一个对象,因为两者共同指向同一个地址。
全栈程序员站长
2022/11/10
14.4K0
JavaScript中的浅拷贝与深拷贝
JS中有两种数据类型,值类型和引用类型,当我们需要把一个变量赋给另一个变量时,对于值类型很简单:
蒋鹏飞
2020/10/15
7780
JavaScript中的浅拷贝与深拷贝
一篇文章彻底搞懂浅拷贝和深拷贝的区别_深拷贝和浅拷贝的题
由博主《前端初级工程师面试系列一JS基础》文章一JS变量类型引伸的考点,变量类型分为基本类型和引用类型,那么在变量拷贝赋值时,也是不一样的,分为浅拷贝和深拷贝,是面试中常考的知识点,也是实际开发中经常会用到的内容。
全栈程序员站长
2022/11/10
5320
一篇文章彻底搞懂浅拷贝和深拷贝的区别_深拷贝和浅拷贝的题
《现代Javascript高级教程》JavaScript深拷贝与浅拷贝
在JavaScript中,对象的拷贝是一项常见的操作。浅拷贝和深拷贝是两种常用的拷贝方式。浅拷贝只复制对象的引用,而深拷贝创建了一个全新的对象,包含与原始对象相同的值和结构。深拷贝和浅拷贝各有适用的场景和注意事项。本文将详细介绍如何实现一个完整而优雅的深拷贝函数,处理循环引用和特殊类型,优化性能,并探讨深拷贝和浅拷贝的应用场景、注意事项和相关属性。
linwu
2023/07/27
6580
js浅拷贝和深拷贝的区别_前端面试深拷贝和浅拷贝
基本数据类型:Boolean、String、Number、null、undefined 引用数据类型:Object、Array、Function、RegExp、Date等
全栈程序员站长
2022/11/15
1.1K0
js浅拷贝和深拷贝的区别_前端面试深拷贝和浅拷贝
相关推荐
【JS专栏】JS对象的浅拷贝与深拷贝
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验