成功的秘诀在于每天都有所进步,即使只是一点点也可以。- 约翰·C·迪维尔比
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
大家好,我是siuuuuuuuuu。
在如今的互联网大环境下,每个人都岌岌可危。本着互联网行业天然的高波动性, 有可能你上午还在工位摸鱼,下午HR
已经给你单独开小灶,通知你提前毕业了。所以在当前的互联网环境,每天刷八股文变得很有必要, 因为你永远都会随时被毕业.
该圣经系列就是为各位广大的前端从业者准备,该圣经如同武林中绝世武学. 读完此文,融会贯通后, 拳打面试官,脚踢hr, offer拿到手软,不在话下。
废话不多说,接下来进入正题。
JS 深浅复制
JavaScript 中的赋值操作,包括对象和数组,有时候会涉及到深拷贝和浅拷贝。深拷贝指将一个对象或数组完整地复制到另一个对象或数组中,而浅拷贝则只是复制了引用,即两者指向同一块内存地址。下面分别介绍一下深拷贝和浅拷贝。
浅拷贝
浅拷贝可以使用 Object.assign() 方法或扩展运算符(...)来实现。这种方法只复制对象/数组的第一层属性,如果属性值是基本数据类型,则复制的是该数据类型的值;如果属性值是引用类型,则复制的是该值在内存中的地址,也就是两个对象/数组共享引用类型的属性值,修改其中一个对象/数组的属性值,会影响另外一个对象/数组的属性值。
// 对象浅拷贝示例
const obj = { a: 1, b: { c: 2 } };
const copyObj = Object.assign({}, obj);
console.log(copyObj); // { a: 1, b: { c: 2 } }
console.log(obj.b === copyObj.b); // true
// 数组浅拷贝示例
const arr = [1, 2, [3]];
const copyArr = [...arr];
console.log(copyArr); // [1, 2, [3]]
console.log(arr[2] === copyArr[2]); // true
深拷贝
深拷贝可以使用递归或 JSON.parse(JSON.stringify()) 方法来实现。这种方法会递归地复制所有嵌套对象/数组,避免了浅拷贝中共享引用类型属性值的问题。
// 对象深拷贝示例
const obj = { a: 1, b: { c: 2 } };
const copyObj = JSON.parse(JSON.stringify(obj));
console.log(copyObj); // { a: 1, b: { c: 2 } }
console.log(obj.b === copyObj.b); // false
// 数组深拷贝示例
const arr = [1, 2, [3]];
const copyArr = JSON.parse(JSON.stringify(arr));
console.log(copyArr); // [1, 2, [3]]
console.log(arr[2] === copyArr[2]); // false
需要注意的是,使用 JSON.parse(JSON.stringify()) 方法进行深拷贝有一些限制:
- 该方法无法复制函数、正则表达式等特殊对象;
- 当被复制的对象中存在循环引用时,该方法会抛出异常。
总之,浅拷贝和深拷贝各有优缺点,需要根据具体情况选择适合的拷贝方式。在处理层级较浅的数据结构时,使用浅拷贝较为方便;而在层级较深或包含特殊对象时,需要使用深拷贝。
手写代码实现深拷贝
实现深拷贝的一种常用方法是使用递归进行对象/数组的遍历和复制。下面给出一个简单的递归实现深拷贝的函数:
function deepClone(obj) {
// 如果不是对象或数组,则直接返回
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 根据obj的具体类型(是数组还是对象)创建一个空的目标对象/数组
let target = Array.isArray(obj) ? [] : {};
// 遍历obj对象/数组,并递归地复制每个属性值
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
target[key] = deepClone(obj[key]);
}
}
return target;
}
该函数首先判断参数对象的类型,如果不是对象或数组,则直接返回原始值。否则,根据原始对象/数组的具体类型创建一个空的目标对象/数组,然后遍历原始对象/数组的属性并递归地复制每个属性值。最后返回复制完成的目标对象/数组。
需要注意的是,该函数没有处理特殊对象,如函数、正则表达式等,如果需要复制特殊对象,则需要在函数中进行相应处理。
// 处理特殊对象的深拷贝函数示例
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (obj instanceof Date) {
return new Date(obj);
}
let target = new obj.constructor();
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
target[key] = deepClone(obj[key]);
}
}
return target;
}
该函数在判断对象类型时,先特判了函数、正则表达式和日期类型,在调用构造函数创建新对象时,使用了特殊对象的构造函数。