我们在JS程序中需要进行频繁的变量赋值运算,对于字符串、布尔值等可直接使用赋值运算符 “=” 即可,但是对于数组、对象、对象数组的拷贝,我们需要理解更多的内容。

首先,我们需要了解JS的浅拷贝与深拷贝的区别。

我们先给出一个数组:

var arr = ["a","b"];

现在怎么创建一份arr数组的拷贝呢?直接执行赋值运算吗?我们来看看输出结果

var arrCopy = arr;
arrCopy[1] = "c";
arr // => ["a","c"]

可以发现对拷贝数组 arrCopy 进行操作时原数组也相应地被改变了,这就是JS的浅拷贝模式。所以我们可以指出对数组、对象、对象数组进行简单赋值运算只是创建了一份原内容的引用,指向的仍然是同一块内存区域,修改时会对应修改原内容,而有时候我们并不需要这种模式,这就需要对内容进行深拷贝。

1 数组的深拷贝


对于数组的深拷贝常规的有三种方法:

方法一:遍历复制

var arr = ["a", "b"], arrCopy = [];
for (var item in arr) arrCopy[item] = arr[item];
arrCopy[1] = "c";
arr    // => ["a", "b"]
arrCopy // => ["a", "c"]

考虑伪多维数组可以写成函数形式:

function arrDeepCopy(source){
var sourceCopy = [];
for (var item in source) sourceCopy[item] = typeof source[item] === 'object' ? arrDeepCopy(source[item]) : source[item];
return sourceCopy;
}

这种方法简单粗暴,但是利用JS本身的函数我们可以更加便捷地实现这个操作。

方法二:slice()

可以参考 W3School 对 slice() 方法的描述:slice() 方法可从已有的数组中返回选定的元素。

调用格式为:

arrayObject.slice(start,end)

方法返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。该方法并不会修改数组,而是返回一个子数组。

在这里我们的思路是直接从数组开头截到尾:

arrCopy = arr.slice(0);
arrCopy[1] = "c";
arr    // => ["a", "b"]
arrCopy // => ["a", "c"]

可以看出成功创建了一份原数组的拷贝。

方法三:concat()

可以参考 W3School 对 concat() 方法的描述:concat() 方法用于连接两个或多个数组。

调用格式为:

arrayObject.concat(arrayX,arrayX,......,arrayX)

该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。

使用这种方法的思路是我们用原数组去拼接一个空内容,放回的便是这个数组的拷贝:

arrCopy = arr.concat();
arrCopy[1] = "c";
arr    // => ["a", "b"]
arrCopy // => ["a", "c"]

2、对象的深拷贝


对于数组的深拷贝我们有了概念,那么一般对象呢?

我们给出一个对象:

var obj = { "a": 1, "b": 2 };

同样做测试:

var objCopy = obj;
objCopy.b = 3;
obj    // => { "a": 1, "b": 3 }
objCopy // => { "a": 1, "b": 3

同样,简单的赋值运算只是创建了一份浅拷贝。

而对于对象的深拷贝,没有内置方法可以使用,我们可以自己命名一个函数进行这一操作:

var objDeepCopy = function(source){
var sourceCopy = {};
for (var item in source) sourceCopy[item] = source[item];
return sourceCopy;
}

但是对于复杂结构的对象我们发现这个函数并不适用,例如:

var obj = { "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 };

所以需要进行一点修改:

var objDeepCopy = function(source){
var sourceCopy = {};
for (var item in source) sourceCopy[item] = typeof source[item] === 'object' ? objDeepCopy(source[item]) : source[item];
return sourceCopy;
}
var objCopy = objDeepCopy(obj);
objCopy.a.a1[1] = "a13";
obj    // => { "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 }
objCopy // => { "a": { "a1": ["a11", "a13"], "a2": 1 }, "b": 2 }

3、对象数组的深拷贝


如果再考虑更奇葩更复杂的情况,例如我们定义:

var obj = [{ "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 5 }]];

这是一个由对象、数组杂合成的奇葩数组,虽然我们平时写程序基本不可能这么折腾自己,但是可以作为一种特殊情况来考虑,这样我们就可以结合之前说的方法去拓展拷贝函数:

var objDeepCopy = function (source) {
var sourceCopy = source instanceof Array ? [] : {};
for (var item in source) {
sourceCopy[item] = typeof source[item] === 'object' ? objDeepCopy(source[item]) : source[item];
}
return sourceCopy;
}
var objCopy = objDeepCopy(obj);
objCopy[0].a.a1[1] = "a13";
objCopy[1][1].e = "6";
obj    // => [{ "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 5 }]]
objCopy // => [{ "a": { "a1": ["a11", "a13"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 6 }]]

这样它就可以作为一个通用函数替我们进行深拷贝操作了。

JS中深拷贝数组、对象、对象数组方法(转载)的更多相关文章

  1. JS 中深拷贝的几种实现方法

    JS 中深拷贝的几种实现方法1.使用递归的方式实现深拷贝 //使用递归的方式实现数组.对象的深拷贝 function deepClone1(obj) { //判断拷贝的要进行深拷贝的是数组还是对象,是 ...

  2. JS中的内置对象简介与简单的属性方法

    JS中的数组: 1.数组的概念: 数组是在内存中连续存储的多个有序元素的结构,元素的顺序称为下标,通过下标查找对应元素 2.数组的声明: ①通过字面量声明var arr1 = [,,,,] JS中同一 ...

  3. JavaScript -- 时光流逝(六):js中的正则表达式 -- RegExp 对象

    JavaScript -- 知识点回顾篇(六):js中的正则表达式 -- RegExp 对象 1. js正则表达式匹配字符之含义      查找以八进制数 规定的字符.     查找以十六进制数 规定 ...

  4. JS中的唯一容器:数组

    JS中的唯一容器:数组 一.什么类型的数据都可以存储 二. 定义的方式有两种  1 .    var  arra=[];var  arra=[“a”,“b”,"c"];   2.v ...

  5. js中内置有对象

    statpot:使用mongo+bootstrap+highcharts做统计报表 最近做了一个统计项目,这个统计项目大致的需求是统计接口的访问速度.客户端会调用一个接口来记录接口的访问情况,我的需求 ...

  6. JS中几种常见的数组算法(前端面试必看)

    JS中几种常见的数组算法 1.将稀疏数组变成不稀疏数组 /** * 稀疏数组 变为 不稀疏数组 * @params array arr 稀疏数组 * @return array 不稀疏的数组 */ f ...

  7. php中向前台js中传送一个二维数组

    在php中向前台js中传送一个二维数组,并在前台js接收获取其中值的全过程方法: (1),方法说明:现在后台将数组发送到前台 echo json_encode($result); 然后再在js页面中的 ...

  8. Js中常用的字符串,数组,函数扩展

    由于最近辞职在家,自己的时间相对多一点.所以就根据prototytpeJS的API,结合自己正在看的司徒大神的<javascript框架设计>,整理了下Js中常用一些字符串,数组,函数扩展 ...

  9. js中的splice方法和slice方法简单总结

    slice:是截取用的 splice:是做删除 插入 替换用的 slice(start,end): 参数: start:开始位置的索引 end:结束位置的索引(但不包含该索引位置的元素) 例如: va ...

  10. js中判断数据类型的四种方法总结

    js中判断数据类型的四种方法 前言 在js中,我们经常需要判断数据的类型,那么哪些方法可以用来判断数据的类型呢?哪种方法判断数据类型最准确呢? 我们来一个个分析: 1.typeof typeof是一个 ...

随机推荐

  1. lapis 处理接收到的json 数据

     备注:      在restful api 开发过程中,大家一般使用的都是json 格式的数据lapis       在处理json 数据上也是比较方便的   1. 使用的api 说明 local ...

  2. 在 CentOS 7.2 上安装 ODOO 10 (2018-10-09 持续更新)

    在 CentOS 7.2 上安装 ODOO 10 更新系统 yum update 安装 EPEL 源 1 yum install -y epel-release 安装依赖组件 yum install ...

  3. 黄聪:VS2010中“新建项目”却没有“解决方案”节点,如何调出来

    工具->选项->项目和解决方案 把"总是显示解决方案"打 √ 就ok 了

  4. WebApi FormData+文件长传 异步+同步实现

    // POST api/values public async Task Post() { try { // 检查该请求是否含有multipart/form-data if (!Request.Con ...

  5. FPGA之外,了解一下中断

    中断是什么? 中断的汉语解释是半中间发生阻隔.停顿或故障而断开.那么,在计算机系统中,我们为什么需要“阻隔.停顿和断开”呢? 举个日常生活中的例子,比如说我正在厨房用煤气烧一壶水,这样就只能守在厨房里 ...

  6. 简单的SOCKET例子

    定义实例socket.socket(),如果括号里不写参数,默认为IPV4+TCP 我们猜测客户端的完整代码如下: 同样我们猜测服务端的代码如下: 实际上运行客户端代码: 说明在py3里,网络编程发送 ...

  7. Java死锁以及命令检测

    Java每个对象都有一把锁,当前进程使用对象锁1,没有释放该锁,又想要去获取另一把对象锁2,而对象锁2被另外一个线程持有,没有释放,这就很容易出现死锁 1.死锁实例 public class Dead ...

  8. Disconf实践指南:使用篇

    在上一篇文章Disconf实践指南:安装篇介绍了如何在本地搭建Disconf环境,下面我们介绍如何在项目中使用Disconf.由于某些功能特性对源码做了修改,所以在官方文档并没有提及. 环境基于mac ...

  9. 安装完Apache后,配置httpd.conf来使apache来加载php模块

    以apache模块的方式来安装php,在httpd.conf文件中首先使用LoadModule php5_module '.../php5apache2.dll'来动态装载Php模块,然后再用语句Ad ...

  10. 用python3判断一个字符串 包含 中文

    在python中一个汉字算一个字符,一个英文字母算一个字符 用 ord() 函数判断单个字符的unicode编码是否大于255即可. s = '我xx们的88工作和生rr活168' n = 0 for ...