JS深拷贝递归实现及解析

638 阅读2分钟

1.为什么要进行深拷贝?

从JS的数据类型说起,JS的数据类型分为值类型和引用类型。

值类型存储在栈中,数据的拷贝直接复制;

引用类型存储在堆栈中,当进行复制时只会拷贝内存中的引用地址,指向的是同一个。 所以如果直接进行拷贝,得到的是内存的引用,如果直接改变会改变另一个的值。如下:

const obj1 = {
    name:"shevon",
    age:12,
    likes:{
        type:"number",
        check:true,
        study:{
            music:"songs"
        }
    }
}

const obj2 = obj1;
obj2.likes.type = "string";
console.log(obj1.likes.type) //返回值string(被改变)

所以如果是引用类型,我们无法直接拷贝,会引起另一个值的改变。

那么如何区分值类型和引用类型?

2.区分值类型和引用类型

JS中提供了typeof方法进行区分。

  • typeof 可以区分所有的值类型
  • typeof 可以识别函数
  • typeof 判断是否是引用类型(不能细分,object array null 都返回object)
typeof "John"                // 返回 string  
typeof 3.14                  // 返回 number  
typeof false                 // 返回 boolean  
typeof [1,2,3,4]             // 返回 object  
typeof {name:'John', age:34// 返回 object
typeof null                  // 返回 object

区分出了引用类型后,我们就可以对引用类型进行处理。

3.如何递归实现深拷贝?

3-1 为什么要用递归实现?

根据JS数据类型的存储机制,值类型的拷贝可以直接实现,多层嵌套的引用类型都可以拆分成最基础的值类型的赋值操作。

3-2 递归怎么实现?

  • 1.确定递归函数的参数和返回值
  • 2.确定终止条件
  • 3.确定单层递归的逻辑
function deepClone(obj = {}){
    //2.终止条件 判断是否是值类型,值类型终止
    if(typeof obj !== "object" || obj == null){
        return obj;
    }
    //判断对象还是数组
    let result;
    if(obj instanceof Array){
        result = [];
    }else{
        result = {};
    }

    for (let key in obj) {
        console.log("循环",key,obj[key])
        if (obj.hasOwnProperty(key)) 
            //递归入口
            //1.参数:引用类型对象  返回值:值类型
            //3.单层递归逻辑 循环当前对象value中的值,赋值给新对象的key
            result[key] = deepClone(obj[key]);
        }
    }
    return result; 
}

3-3 引申 instanceof 和 hasOwnProperty

instanceof

  • 检测某个对象是否属于某个构造函数(原型相关)
  • 用法:instanceof object instanceof constructor
  • 可以更细化区分上述typeof区分的引用类型中更细致的对象类型。
const obj = new Object()
const dat = new Date()
const fun = new Function()
const reg = new RegExp()
const arr = new Array()

console.log('obj', obj instanceof(Object)) //true
console.log('dat', dat instanceof(Date)) //true
console.log('fun', fun instanceof(Function)) //true
console.log('reg', reg instanceof(RegExp)) //true
console.log('arr', arr instanceof(Array)) //true

hasOwnProperty

  • 检测属性是否为对象的自有属性
  • 用法object.hasOwnProperty(propertyName) // true/false
  • 每个对象上都有自己的原型Prototype属性,遍历对象需要做区分