JavaScript的浅拷贝与深拷贝的实现方法

深拷贝与浅拷贝是JavaScript中非常重要的概念,它们在对象和数组等引用类型的复制过程中发挥着重要作用。本文将详细介绍深拷贝和浅拷贝的概念、应用场景和实现方法。

拷贝

在JavaScript中,拷贝是指将一个变量或对象的值赋给另一个变量或对象,使它们的值相同。在JavaScript中,拷贝通常只针对引用类型,如对象和数组等。

浅拷贝

浅拷贝是指创建一个新对象,这个新对象有着原始对象的属性值。如果属性是基本数据类型,拷贝的就是基本数据类型的值;如果属性是引用数据类型,拷贝的就是内存地址,也就是指向堆内存中同一个对象的指针。因此,如果其中一个对象改变了这个指针所指向的内存中的值,另一个对象也会受到影响。

浅拷贝的实现方法

常见的浅拷贝方法有:

Object.create(obj)

该方法会创建一个新对象,新对象的原型链指向obj,并返回新对象。这种方式的缺点是只能克隆原始对象的属性,不能克隆原始对象的方法。

javascript 复制代码
let obj = {a: 1, b: {c: 2}};
let newObj = Object.create(obj);
console.log(newObj); // {}
console.log(newObj.a); // 1
console.log(newObj.b); // {c: 2}

Object.assign({}, obj)

assign() 方法原本用于将一个或多个源对象的所有可枚举属性复制到目标对象中,并返回目标对象。如果目标对象中已经有相同的属性,则会覆盖原有的属性值,所以可以用来浅拷贝对象。这种方式的缺点是只能克隆原始对象的自身属性,不能克隆原始对象的继承属性和方法。

javascript 复制代码
let obj = {a: 1, b: {c: 2}};
let newObj = Object.assign({}, obj);
console.log(newObj); // {a:1,b:{c:2}}
console.log(newObj.b === obj.b); // true

[].concat(arr)

concat() 方法原本用于合并两个或多个数组,但这个方法不会改变现有的数组,而是返回一个新数组,包含了所有被合并的数组中的元素,所以可以用来浅拷贝数组。这种方式的缺点是只能克隆一维数组,不能克隆多维数组和对象,而且这种方式只会复制源对象的自身属性,而不会复制继承属性。

javascript 复制代码
let arr = [1, 2, {a: 3}];
let newArr = [].concat(arr);
console.log(newArr); // [1,2,{a:3}]
console.log(newArr[2] === arr[2]); // true

数组解构[...arr]

该方法会将数组展开成多个元素,然后将这些元素赋值给新的数组。这种方式的缺点同样是只能克隆一维数组,不能克隆多维数组和对象。这种方式也只会复制源对象的自身属性,而不会复制继承属性。

javascript 复制代码
let arr = [1, 2, {a: 3}];
let newArr = [...arr];
console.log(newArr); // [1,2,{a:3}]
console.log(newArr[2] === arr[2]); // true

arr.toReversed().reverse()

该方法会将原始数组拷贝一份,然后反转两次得到新的数组。toReversed()会返回一个元素顺序相反的新数组。这种方式的缺点同样是只能克隆一维数组,不能克隆多维数组和对象。

javascript 复制代码
let arr = [1, 2, {a: 3}];
let newArr = arr.toReversed().reverse(); // 抛出异常TypeError: arr.toReversed is not a function
console.log(newArr);

浅拷贝的应用场景

浅拷贝适用于需要复制对象或数组的场景,但不需要改变原始对象。例如,当我们需要在多个地方使用同一个对象时,可以使用浅拷贝来复制这个对象。这样,我们就可以在多个地方使用同一个原始对象,而不用担心修改原始对象的影响。

深拷贝

深拷贝是指创建一个新对象,这个新对象与原始对象有着相同的属性和值,但是它们所对应的内存地址不同。也就是说,深拷贝会将原始对象的所有属性(包括嵌套的对象和数组等)都递归复制一份,从而实现真正的复制。

深拷贝的实现方法

常见的深拷贝方法有:

JSON.parse(JSON.stringify(obj))

该方法是将熟悉的JavaScript对象转换为JSON字符串,再将JSON字符串转换为新的JavaScript对象。这种方式的缺点是无法处理 undefined、function、Symbol 等数据类型,而且无法处理循环引用。

javascript 复制代码
let obj = {a: 1, b: {c: 2}};
let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj); // {a:1,b:{c:2}}
console.log(newObj.b === obj.b); // false

递归实现

递归实现是一种较为通用的深拷贝方式。它会遍历对象的每个属性,并将其递归地复制到新对象中。这种方式相对于JSON.parse(JSON.stringify(obj))来说,可以处理 undefined、function 和 Symbol 等数据类型,但仍然无法处理循环引用。

javascript 复制代码
function deepClone(obj) {
    let newObj = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (typeof obj[key] === 'object') {
                newObj[key] = deepClone(obj[key]);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    return newObj;
}
let obj = {a: 1, b: {c: 2}};
let newObj = deepClone(obj);
console.log(newObj); // {a:1,b:{c:2}}
console.log(newObj.b === obj.b); // false

第三方库实现

当我们需要处理循环引用时,可以使用第三方库来实现深拷贝。例如,lodash、underscore 和 jQuery 等库都提供了深拷贝的方法。

javascript 复制代码
let obj = {a: 1, b: {c: 2}};
let newObj = _.cloneDeep(obj);
console.log(newObj); // {a:1,b:{c:2}}
console.log(newObj.b === obj.b); // false

深拷贝的应用场景

深拷贝适用于需要完全复制对象或数组的场景,同时需要保留原始对象的所有属性和方法。例如,在多个地方使用同一个对象时,如果我们需要对这个对象进行修改,就可以使用深拷贝来避免影响到其他地方对原始对象的使用。

总结

在JavaScript中,拷贝是指将一个变量或对象的值赋给另一个变量或对象,使它们的值相同。浅拷贝是指创建一个新对象,这个新对象有着原始对象的属性值的一份精确拷贝。深拷贝是指创建一个新对象,这个新对象与原始对象有着相同的属性和值,但是它们所对应的内存地址不同。

常见的浅拷贝方法包括 Object.create(obj)、Object.assign({}, obj)、[].concat(arr)、数组解构[...arr] 和 arr.toReversed().reverse()。常见的深拷贝方法包括 JSON.parse(JSON.stringify(obj))、递归实现和第三方库实现。在使用深拷贝时,需要注意处理循环引用问题。

根据不同的需求,我们可以选择合适的拷贝方式来复制对象或数组,从而提高代码的可维护性和可读性。

相关推荐
程序猿阿伟8 小时前
《首屏加载优化手册:Vue3+Element Plus项目提速的技术细节》
前端·javascript·vue.js
尘觉9 小时前
面试-浅复制和深复制?怎样实现深复制详细解答
javascript·面试·职场和发展
chxii10 小时前
ISO 8601日期时间标准及其在JavaScript、SQLite与MySQL中的应用解析
开发语言·javascript·数据库
没逛够11 小时前
Vue 自适应高度表格
javascript·vue.js·elementui
咖啡の猫13 小时前
Vue初始化脚手架
前端·javascript·vue.js
拉不动的猪14 小时前
如何处理管理系统中(Vue PC + uni-app 移动端):业务逻辑复用基本方案
前端·javascript·架构
边洛洛14 小时前
next.js项目部署流程
开发语言·前端·javascript
非凡ghost15 小时前
Syncovery Premium(文件同步软件)
前端·javascript·后端
神膘护体小月半15 小时前
bug 记录 - 路由守卫 beforeRouteLeave 与 confirm 结合,不生效问题
javascript·vue
岁月宁静15 小时前
Vue 3.5 + WangEditor 打造智能笔记编辑器:语音识别功能深度实现
前端·javascript·vue.js