(一) 谈一谈深克隆和浅克隆
(1)浅克隆(只复制内存地址):
- 浅克隆:只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用
- 换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。
(2)深克隆(和原来的没关系, 创建一个新对象)
- 属性中引用的其他对象也会被克隆,不再指向原有对象地址。
JSON.parse、JSON.stringify()
(二) 手写浅拷贝、深拷贝
题目:
1. 浅拷贝(和原来的有关系):
只是把对象的属性和属性值拷贝到另一个对象中,只克隆第一层,没有克隆地址
(1)方法一(ES6)
let obj2 = { ...obj };
console.log(obj, obj2);
(2)方法二(ES6):Object.assign()
Object.assign:会合并对象生成一个新对象。
如果对象的属性是普通类型改变之后新对象不会改变,如果是引用类型改变后新对象也会改变,所以Object.assign实际上还是浅拷贝。
var obj={
aa:1,
b:{item:'45'}
};
var newObj=Object.assign({},obj);
obj.aa=2;//对象的属性是普通类型改变之后新对象不会改变
obj.b.item='kk';//是引用类型改变后新对象也会改变
console.log(newObj.aa); //1
console.log(newObj.b.item); //kk
(3)方法三
- 复制非继承属性(hasOwnProperty())
- 只复制第一层
function shallowClone(obj) {
let obj3 = {};
for (let key in obj) { // for...in 循环遍历所有可枚举属性
if (!obj.hasOwnProperty(key)) break;//判断一个对象里是否含有某个非继承属性
obj3[key] = obj[key];
}
// console.log(obj, obj3);
return obj;
}
2. 深拷贝:
(1)方法一
JSON.stringify():将 JavaScript 值转换为JSON 字符串。JSON.parse:JSON.parse():方法用于将一个SON 字符串转换为对象。let obj4 = JSON.parse(JSON.stringify(obj));//弊端
(2)方法二
- JavaScript constructor 属性详解
- prototype的属性值中天生自带一个constructor属性, 其constructor属性值指向当前原型所属的类
- 其实深拷贝可以拆分成 2 步:浅拷贝 + 递归;浅拷贝时判断属性值是否是对象,如果是对象就进行递归操作,两个一结合就实现了深拷贝。
关键点:
- 注意判断值类型和引用类型
- 值类型(
typeof):直接返回 - 引用类型(
instanceof):先判断其他具体的对象类型
- 值类型(
- 引用类型,判断是数组还是对象
- 创建空对象newObj,改变内存地址的指向
- 数组:
newObj = [] - 对象:
newObj = new obj.constructor()
- 数组:
- 遍历、递归
- 返回数据 代码:
function deepClone(obj) {
// 【1】. 判断值类型和引用类型:
// 非对象、数组:typeof来判断对象和数组,因为这种类型都会返回object
if (obj === null || typeof obj !== 'object') return obj;
// 可通过 instanceof 操作符来判断对象的具体类型,或是否是某个对象的实例
if (obj instanceof RegExp) {
return new RegExp(obj); // 正则对象的实例
}
if (obj instanceof Date) {
return new Date(obj); // 日期对象的实例
}
// function
// ...
// 【2】. 判断是数组还是对象
let newObj; // 创建空对象
if (obj instanceof Array) {
newObj = []; // 创建空数组,改变内存地址,不再指向被复制的内存地址
} else {
/*
为什么不直接用“newObj= new Object();”创建空对象?
目的:用constructor可以使克隆的结果和之前保持相同的所属类
obj.constructor= Object
*/
// let newObj = new obj.constructor;//既能克隆对象也能克隆实例;
newObj = new obj.constructor(); // 创建空对象,改变内存地址
}
for (let key in obj) {
//保证key不是原型的属性
if (obj.hasOwnProperty(key)) {
// 【3】. 递归
newObj[key] = deepClone(obj[key]);
//newObj[key] = arguments.callee(obj[key]);
//使用arguments.callee就可以让函数逻辑与函数名解耦
}
}
return newObj;
}
测试对象:
// 测试对象
let obj5 = deepClone(obj);
console.log(obj, obj5);
console.log('obj === obj5', obj === obj5); //false
console.log('obj.c === obj5.c', obj.c === obj5.c); //false
测试数组:
//测试数组
let arr2 = deepClone(arr);
console.log(arr, arr2);
console.log('arr === arr2', arr === arr2); //false
console.log('arr[0] === arr2[0]', arr[0] === arr2[0]); //false