JavaScript之深拷贝和浅拷贝

94 阅读3分钟

前置知识

众所周知,数据分为基本数据类型(String, Number ,Boolean, Null, Undefined ,Symbol)和对象数据类型

基本数据类型的特点:直接声明的变量会存储在中,如果该数据是全局变量,会放在

对象数据类型的特点:存储的是该对象在中的引用,真实的数据存放在堆内存中;存储的对象是一个地址,地址指向堆内存中的某个对象

浅拷贝

定义:按位拷贝对象,创建一个新对象,这个对象有着原始对象属性的一份拷贝。

对于基本数据类型来说,浅拷贝后创建一个新的对象属性,修改新的对象属性不影响原始对象的属性。

对于引用数据类型来说,浅拷贝后创建一个新的对象属性,修改新的对象属性影响原始对象的属性。

五种浅拷贝的方式

1.Object.assign([target],source)

Object.assign(obj,source);
const obj=Object.assign(source);

注意点:

  • 它不会拷贝对象的继承属性;
  • 它不会拷贝对象的不可枚举的属性;
  • 可以拷贝 Symbol 类型的属性。

2.扩展运算符

const obj={...source};
//和Object.assign功能相同,注意点也相同

3.Array.prototype.concat

const arr=[1,2,{name:'mike'}];
const newArr=arr.concat();
newArr[2].name='tom'
//浅拷贝,当数组中含有引用类型数据时,修改原数组中的元素的属性会影响拷贝后连接的数组

4.Array.prototype.slice

const arr = [1, 2, {name: 'nordon'}]; 
const newArr = arr.slice(); 
newArr[2].name = 'wy';
//同上

5.使用第三方库&手动实现

Object.prototype.hasOwnProperty()

判断某个对象是否含有指定的自身属性(不包含继承的属性!)

Object.prototype.isPrototypeOf(obj)

测试一个对象是否在另一个对象的原型链上(测试一个对象是否是另一个的原型)

const clone = (source) =>{
    if(typeof source === 'object' && source !== null){
        const cloneTarget = Array.isArray(source) ? []:{};
        //for in可以遍历对象的所有属性包括继承的
        for(let prop in source){
            if(source.hasOwnProperty(prop)){
                cloneTarget[prop]=source[prop];
            }
        }
    }
    else return source;
}

深拷贝

如果深拷贝,那么拷贝的对象和原始对象就再也没有关系了,除了它们的属性值相同。

实现方式

1.JSON.parse(JSON.stringify(obj))通过JSON的2次转换深拷贝obj,不过无法拷贝undefinedsymbol属性,无法拷贝循环引用对象

简单来说,循环引用就是你引用我,我引用你

2.使用第三方工具库库

3.自己实现一个深拷贝函数

递归调用浅拷贝函数

const deepClone = (source,map = new Map()) =>{
    //基本数据类型直接返回值
    if(typeof source !== 'object'|| source === null )return source;
    //若已经clone,返回clone的对象
    if(s=map.get(source))return s;
    //生成clone对象
    const clone = Array.isArray(source)?[]:{};
    for(let prop in source){
clone[prop]=deepClone(source[prop],map);
    }
    map.set(source,clone);
    return clone;
}

进阶版

const isObject = (target) =>
        (typeof target === "object" || typeof target === "function") &&
        target !== null;
        //考虑到正则类/日期类等特殊对象
function deepclone_pro(target, map = new Map()) {
        if (map.get(target)) return target;
        if (!target || (typeof target !== "object")) return target;
        let constructor = target.constructor;
        // 检测当前对象target是否与 正则、日期、函数格式对象匹配
        if (/^(RegExp|Date)$/i.test(constructor.name)) {
          return new constructor(target); // 创建一个新的特殊对象(正则类/日期类)的实例
        }

        if (isObject(target)) {
          map.set(target, true); // 为循环引用的对象做标记
          let newObj = Array.isArray(target) ? [] : {};
          for (let prop in target) {
            if (target.hasOwnProperty(prop)) {
              newObj[prop] = deepclone_pro(target[prop], map); //map为循环引用的对象做标记,防止无限递归
            }
          }
        }
        return newObj
}