目录
深拷贝
解释
深拷贝是一种复制对象的方法,它会创建一个新的对象,与原始对象完全独立,并且递归地将原始对象的所有属性进行复制。
即如果原始对象包含其他对象或引用类型的属性,深拷贝将递归复制它们,因此在新对象中,新的复合对象将具有原始对象所有属性的副本。因此,原始对象和新对象的修改不会相互影响。
手写实现
1. 通过 lodash 的 cloneDeep() 也可以直接调用实现;
2. 通过 JSON.parse(JSON.stringify(...)) 序列化实现,缺点:数据丢失;
- ① 不能拷贝函数
- ② 不能拷贝 Symbol()
- ③ 不能拷贝循环引用的对象(会报错,比如 window.window.window.window )
- ④ key 的值为 undefined 不会拷贝
3. 手写递归判断
// 手写实现
function deepClone(obj) {
// 基本类型 或 空
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 引用类型
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
// 只复制自身属性,忽略原型链上的可遍历属性
if (obj.hasOwnProperty(key)) {
// 基本类型 或 空
if (typeof obj[key] !== 'object' || obj[key] === null) {
newObj[key] = obj[key];
}
// 引用类型
else {
newObj[key] = deepClone(obj[key]);
}
}
}
return newObj;
}
测试用例
const obj1 = {
name: 'Tom',
age: 20,
hobbies: ['reading', 'music', 'sports'],
address: {
city: 'Beijing',
district: 'Haidian'
}
};
const obj2 = deepClone(obj1);
obj2.hobbies.push('travel');
obj2.address.city = 'Shanghai';
console.log(obj1); // {name: 'Tom', age: 20, hobbies: ['reading', 'music', 'sports'], address: {city: 'Beijing', district: 'Haidian'}}
console.log(obj2); // {name: 'Tom', age: 20, hobbies: ['reading', 'music', 'sports', 'travel'], address: {city: 'Shanghai', district: 'Haidian'}}
浅拷贝
解释
浅拷贝指的是创建一个新对象,这个对象具有原始对象属性值的副本。如果属性是原始值类型,则拷贝的是值的副本;如果属性是引用类型,则拷贝的是内存地址的副本。
也就是说,新对象和原始对象的这个属性指向同一个内存地址(复制指针)。因此,原始对象和新对象的修改会相互影响。
可以看成最外层是深拷贝的,而遇到深层便浅拷贝了。
手写实现
// 浅拷贝有很多方法
// 1. 使用 Object.assign()
const obj2 = Object.assign({}, obj1)
// 2. 使用展开运算符
const obj3 = {...obj1}
// 3. 针对数组可使用不带参数的 Array.prototype.slice 或 Array.prototype.concat
const arr3 = arr1.slice()
const arr4 = arr1.concat()
// 4. 手写
function myClone(obj) {
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
其它注意事项
- 在 IE8 以下不支持 Array.isArray() ,需要手写实现。
if (!Array.isArray) {
Array.isArray = function(arguments) {
return Object.prototype.toString.call(arguments) === '[object Array]';
}
}
- 为什么使用不用 instanceof 来进行判断:
- instanceof 是检查构造函数的 prototype 是否出现在对应实例的原型链上来实现的,也就是说如果手动更改了 __proto__ 指向,也会被判定到。
- 无法正确判断 Array.prototype 是否是数组
console.log(Array.prototype instanceof Array);
// 输出 false 实际 true
const obj = { a: 1, __proto__: Array.prototype };
console.log(obj instanceof Array);
// 输出 true 实际 false