JS赋值、浅拷贝和深拷贝

本文详细介绍了JavaScript中赋值、浅拷贝和深拷贝的概念及其区别,包括Array.concat(), Object.assign()以及扩展运算符等实现浅拷贝的方法。此外,还探讨了深拷贝的实现,如JSON.parse(JSON.stringify()), jQuery.extend()以及递归实现,并解决了循环引用问题。最后,文章提及了处理symbol类型的方法,确保深拷贝的全面性。
摘要由CSDN通过智能技术生成


数组和对象直接赋值存在问题:同一个Array或者Object赋值给两个不同变量时,变量指向的是同一个内存地址,所以就会造成其中一个变量改变属性值,同时改变了另外一个变量的对应属性值。

​ 而大多数实际项目中,我们想要的结果是两个变量(初始值相同)互不影响。所以就要使用到拷贝(分为深浅两种)

一、赋值

​ 赋值是将某一数值或对象赋给某个变量的过程,分为:

​ 1、基本数据类型:赋值,赋值之后两个变量互不影响

​ 2、引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有影响

二、浅拷贝

​ 创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

​ 简单来说可以理解为浅拷贝只解决了第一层的问题,拷贝第一层的基本类型值,以及第一层的引用类型地址

几种浅拷贝方法:

​ (1)Array.concat(),Array.slice(),Array.from()

​ (2)Object.assign()

​ (3)es6扩展运算符

三、简单的深拷贝:

​ 深拷贝是逐层对目标对象进行复制,意味着会在栈内存中重新分配空间存储指向一个新对象的新地址指针,因此不存在改变一个对象值而引发另一个对象随之改变的问题。

1.通过 JSON 对象实现深拷贝

function deepClone2(obj) {
  objClone = JSON.parse(JSON.stringify(obj))
  return objClone;
}

​ 问题:

​ undefined、任意的函数以及 symbol值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined

2.通过jQuery的extend方法实现深拷贝

var array = [1,2,3,4];
var newArray = $.extend(true,[],array);

3.递归调用

function deepClone1(obj) {
  //判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
  var objClone = Array.isArray(obj) ? [] : {};
  //进行深拷贝的不能为空,并且是对象或者是
  if (obj && typeof obj === "object") {
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (obj[key] && typeof obj[key] === "object") {
          objClone[key] = deepClone1(obj[key]);
        } else {
          objClone[key] = obj[key];
        }
      }
    }
  }
  return objClone;
}

四、包含处理循环引用问题的深拷贝

​ 循环引用问题的产生原因可能是对象之间相互引用,也可能是对象引用了其自身,而造成死循环。解决方法是将这些引用存储起来并在发现引用时返回被引用过的对象,从而结束递归的调用。
方法一:
使用ES6新增WeakMap

function deepClone( originObj, map = new WeakMap() ) {
    if(!originObj || typeof originObj !== 'object') return originObj;  //空或者非对象则返回本身

    if( map.get(originObj) ) {      //如果这个对象已经被记录则直接返回
        return  map.get(originObj);
    }
    
    //将其引用记录在map中,进行拷贝
    let result = Array.isArray(originObj) ? [] : {};  //初始化拷贝结果
    map.set(originObj, result);                       //记录引用关系
    let keys = Object.keys(originObj);                //originObj的全部key集合
    for(let i =0,len=keys.length; i<len; i++) {       //递归拷贝
        let key = keys[i];
        let temp = originObj[key];
        result[key] = deepClone(temp, map);
    }
    return result;
}

方法二:
es5使用方法

function deepClone(source, uniqueList) {

    if (!isObject(source)) return source; 
    if (!uniqueList) uniqueList = []; // 新增代码,初始化数组
      
    var target = Array.isArray(source) ? [] : {};
    
    // ============= 新增代码
    // 数据已经存在,返回保存的数据
    var uniqueData = find(uniqueList, source);
    if (uniqueData) {
        return uniqueData.target;
    };
        
    // 数据不存在,保存源数据,以及对应的引用
    uniqueList.push({
        source: source,
        target: target
    });
    // =============

    for(var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            if (isObject(source[key])) {
                target[key] = cloneDeep3(source[key], uniqueList); // 新增代码,传入数组
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

五、考虑symbol类型

​ Symbol 在 ES6 下才有,我们需要一些方法来检测出 Symble 类型。

方法一:Object.getOwnPropertySymbols(…)
方法二:Reflect.ownKeys(…)

​ 思路就是先查找有没有 Symbol 属性,如果查找到则先遍历处理 Symbol 情况,然后再处理正常情况,多出来的逻辑就是下面的新增代码。

function cloneDeep4(source, map = new WeakMap()) {

    if (!isObject(source)) return source; 
    if (map.get(source) return map.get(source); 
      
    let target = Array.isArray(source) ? [] : {};
    hash.set(source, target);
    
    // ============= 新增代码
    let symKeys = Object.getOwnPropertySymbols(source); // 查找
    if (symKeys.length) { // 查找成功	
        symKeys.forEach(symKey => {
            if (isObject(source[symKey])) {
                target[symKey] = cloneDeep4(source[symKey], map); 
            } else {
                target[symKey] = source[symKey];
            }    
        });
    }
    // =============
    
    for(let key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            if (isObject(source[key])) {
                target[key] = cloneDeep4(source[key], map); 
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

参考文章:

浅析JavaScript解析赋值、浅拷贝和深拷贝的区别 - saucxs - 博客园 (cnblogs.com)

浅拷贝与深拷贝的实现及深拷贝的循环引用问题 - vickylinj - 博客园 (cnblogs.com)

如何实现一个深拷贝(考虑循环引用对象、和symbol类型)_Dream_Lee的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值