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))、递归实现和第三方库实现。在使用深拷贝时,需要注意处理循环引用问题。

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

相关推荐
勿语&19 分钟前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web5 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
Jiaberrr6 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
安冬的码畜日常8 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
太阳花ˉ8 小时前
html+css+js实现step进度条效果
javascript·css·html
john_hjy9 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd9 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome