JavaScript数组与对象的复制:浅拷贝与深拷贝详解

作者:半夏

时间:2026-10-21

浏览:581

本文详细解析JavaScript中基本类型与引用类型的复制机制,重点介绍数组的concat、slice浅拷贝方法及Object.assign的使用。针对深层数据复制,对比递归实现与JSON序列化方案的优劣,指出原型链丢失等潜在问题,帮助开发者根据场景选择合适策略。

文章目录

数据类型与复制机制基础

一、数据类型

从狭义角度分析,JavaScript将数据划分为基本类型和引用类型两大类。基本类型涵盖Undefined、Null、Boolean、Number以及String;而引用类型则统一归为Object,其中Array、Date、RegExp和Function等常用类型均属于Object范畴。

基本类型与引用类型在变量复制时的行为存在显著差异:基本类型会生成一份完全独立的新拷贝,而引用类型仅复制原变量的内存引用。以下代码展示了这一区别:


// 基本类型数据的复制
var a = 10;
var b = a; // b = 10
a = 20; // a = 20, b = 10
// 引用类型数据的复制
var m = [1, 2];
var n = m;
m[0] = 10;
console.log(n[0]); // 10

若需复制引用类型本身的值而非其引用,上述直接赋值的方式显然无法满足需求。

数组的浅复制方法

二、数组的浅复制

浅复制是指当对象(或数组)被复制时,其内部引用字段的值不会被递归复制,而是保留了对原字段值的引用。示例如下:


var src = [
 'alpha',
 ['bravo', 'chalie']
];
var dest = [];
for (var i = 0; i < src.length; i++) {
 dest[i] = src[i];
}
//此时,如果改变src中的引用字段,dest中相应的字段也会被改变
src[1].push('delta');
console.log(dest[1]); // ['bravo', 'chalie', 'delta']

浅复制通常适用于一维数组,即数组内部不包含其他引用类型的情况。常见的浅复制实现方式包括:

concat方法


 var src = ['alpha', 'bravo'],
  dest = [];
 dest = dest.concat(src);

concat方法常用于数组合并操作,例如:


 var a = ['alpha', 'bravo'],
  b = ['chalie', 'delta'],
  combine;
 combine = a.concat(b);

需要特别注意,当concat用于合并多个数组时,会将所有元素复制到新对象中,处理大型数组时性能开销较大。更高效的优化方案是将后一个数组的元素直接追加到前一个数组中:


 var src = ['alpha', 'bravo'],
  dest = ['chalie', 'delta'];
 Array.prototype.push.apply(src, dest);

slice方法

slice方法可从现有数组中提取选定元素,并返回一个包含这些元素的新数组。


 var src = ['alpha', 'bravo'],
 var dest = src.slice(0);

对象的浅复制实现

三、对象的浅复制

对象的浅复制可通过for-in遍历实现,在ES6标准中,提供了更为便捷的Object.assign()方法。


 var src = {name: 'fox', age: 20},
  dest = null;
 dest = Object.assign({}, src);

此外,也可借助jQuery库中的$.extend或underscore库中的_.extend等方法来完成对象复制。

深度复制策略与局限

四、深度复制

浅复制的应用场景较为有限,多数情况下我们需要生成对象的完整副本,这要求利用typeof或instanceof操作符对各字段类型进行判断。若字段为基本类型,可直接复制;若为引用类型,则需对该字段的所有子字段重复上述判断过程,因此递归是实现该功能的常见思路。


function deep_copy(src, dest) {
 for (var p in src) {
  if (Array.isArray(src[p]) || src[p] instanceof Object) {
   dest[p] = Array.isArray(src[p]) ? [] : {};
   arguments.callee(dest[p], src[p]);
  }else {
   dest[p] = src[p];
  }
 }
}

在上述代码逻辑中,由于数组本质上是特殊的对象,因此可以使用for-in进行遍历处理。

另外,还有一种常用的JSON序列化方案:


 function deep_copy_in_json(src) {
  return JSON.parse(JSON.stringify(src));
 }

尽管该方法实现简便,但原对象的许多属性在序列化过程中会丢失,例如constructor属性以及对象原型链上定义的一些方法。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章