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属性,遍历对象需要做区分