拷贝分为浅拷贝和深拷贝,在JavaScript中能够实现这两种拷贝的方式也是多种多样。以下是一维数组实现深拷贝和浅拷贝的各种方式。

一、浅拷贝

1、赋值

赋值是最直接的一种浅拷贝。

let arr3 = [1,2,3]

let arr4 = arr3

arr4[0] = 11

console.log('arr3:',arr3); // [11,2,3]

2、copyWithin()方法——ES6新增

let arr3 = [1, 2, 3]

let arr4 = arr3.copyWithin(0,0)

arr4[0] = 11

console.log('修改arr4:', arr4); // 11,2,3

console.log('arr3:', arr3); // 11,2,3

二、深拷贝(针对一维纯值数组)

1、concat() 方法

concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。其参数可以是具体的值,也可以是数组对象。可以是任意多个。

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

参数     描述

arrayX  必需。该参数可以是具体的值,也可以是数组对象。可以是任意多个。

利用concat()方法可以得到一个深拷贝的数组副本。

let arr3 = [1,2,3]

let arr4 = arr3.concat();

arr4[0] = 11

console.log('修改arr4:',arr4); //  11,2,3

console.log('arr3:',arr3);  // 1,2,3

2、slice()方法

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

arrayObject.slice(start,end)

参数     描述

start      必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。

end 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。

利用slice()方法可以得到一个深拷贝的数组副本。

let arr3 = [1,2,3]

let arr4 = arr3.slice()

arr4[0] = 11

console.log('修改arr4:',arr4); // 11,2,3

console.log('arr3:',arr3);  // 1,2,3

3、序列化和反序列化方法

利用JSON.parse和JSON.stringify组合得到一个深拷贝的数组副本。

let arr3 = [1,2,3]

let arr4 = JSON.parse(JSON.stringify(arr3))

arr4[0] = 11

console.log('修改arr4:',arr4); // 11,2,3

console.log('arr3:',arr3);  // 1,2,3

注:此方法适用于Oject的深度拷贝,因为Array属于Oject类型,所以也适用于此处;需要注意的是:作为Oject的深度拷贝时,要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。

4、扩展运算符——ES6新增

ES6新增的扩展运算符可以方便的实现数组的深拷贝(克隆)。

let arr3 = [1,2,3]

let arr4 = [...arr3]

arr4[0] = 11

console.log('修改arr4:',arr4);   // 11,2,3

console.log('arr3:',arr3);   // 1,2,3

let arr3 = [1,2,3]

let [...arr4] = arr3

arr4[0] = 11

console.log('修改arr4:',arr4);

console.log('arr3:',arr3);

5、Array.from——ES6新增

let arr1 = [1,2,3]

let arr2 = Array.from(arr1)

arr2[0] = 11

console.log('修改learr2');

console.log('arr1是否变化',arr1); // 验证结果:Array.from返回的是一个深度拷贝数组

6、map()方法

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

map() 方法按照原始数组元素顺序依次处理元素。

注意: map() 不会对空数组进行检测。 map() 不会改变原始数组。

array.map(function(currentValue,index,arr), thisValue)

参数     描述

function(currentValue, index,arr)   必须。函数,数组中的每个元素都会执行这个函数

函数参数:

参数     描述

currentValue      必须。当前元素的值

index    可选。当前元素的索引值

arr  可选。当前元素属于的数组对象

thisValue     可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。

如果省略了 thisValue,或者传入 null、undefined,那么回调函数的 this 为全局对象。

let arr3 = [1,2,3]

let arr4 = arr3.map(item => item)

arr4[0] = 11

console.log('修改arr4:',arr4);  // 11,2,3

console.log('arr3:',arr3);  // 1,2,3

7、Object.assign()方法——ES6新增

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

let arr3 = [1,2,3]

let arr4 = []

Object.assign(arr4,arr3)

arr4[0] = 11

console.log('修改arr4:',arr4); // 11,2,3

console.log('arr3:',arr3);   // 1,2,3

注意:Object.assign()方法实行的是浅拷贝,而不是深拷贝。如果数组中的项是一个对象,那么目标数组中的该项拷贝得到的是这个对象的引用。

let arr3 = [{name:'aaa'},2,3]

let arr4 = []

Object.assign(arr4,arr3)

arr4[0].name = 11

console.log('修改arr4:',arr4);  // {name:11},2,3

console.log('arr3:',arr3);  // {name:11},2,3

8、扩展原型链

Array.prototype.clone = function () {

let arr = []

for (let i = 0, len = this.length; i < len; i++) {

arr.push(this[i])

}

return arr

}

let arr3 = [1, 2, 3]

let arr4 = arr3.clone()

arr4[0] = 11

console.log('修改arr4:', arr4); // 11,2,3

console.log('arr3:', arr3);  // 1,2,3

9、filter方法

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

注意: filter() 不会对空数组进行检测。filter() 不会改变原始数组。

array.filter(function(currentValue,index,arr), thisValue)

参数     描述

function(currentValue, index,arr)   必须。函数,数组中的每个元素都会执行这个函数。返回true表示保留该元素(通过测试),false则不保留

函数参数:

参数     描述

currentValue      必须。当前元素的值

index    可选。当前元素的索引值

arr  可选。当前元素属于的数组对象

thisValue     可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。

如果省略了 thisValue ,"this" 的值为 "undefined"

返回值:返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组。

let arr3 = [1, 2, 3]

let arr4 = arr3.filter(item => true)

arr4[0] = 11

console.log('修改arr4:', arr4); // 11,2,3

console.log('arr3:', arr3);  // 1,2,3

总结

以上各种深拷贝方式只针对一维并且每一项值为数值和字符串的数组。多维数组或者一维但包含对象的数组除了第三种方式外,其他方式并不能实行深拷贝,实行的是浅拷贝。

三、多维数组的深拷贝

function deepcopy(obj) {

let out = [],len = obj.length;

for (let i = 0; i < len; i++) {

if (obj[i] instanceof Array){

out[i] = deepcopy(obj[i]);

} else {

out[i] = obj[i];

}

}

return out;

}

另一种方式:采用上面的第三种方式:序列化和反序列化结合实现。

前端学习笔记系列一:9 js中数组的拷贝的更多相关文章

  1. 前端学习笔记系列一:12 js中获取时间new date()的用法

    获取时间: 1  var myDate = new Date();//获取系统当前时间 获取特定格式的时间: 1 myDate.getYear(); //获取当前年份(2位) 2 myDate.get ...

  2. Javascript高级编程学习笔记(3)—— JS中的数据类型(1)

    前一段时间由于事情比较多,所以笔记耽搁了一段时间,从这一篇开始我会尽快写完这个系列. 文章中有什么不足之处,还望各位大佬指出. JS中的数据类型 上一篇中我写了有关JS引入的Script标签相关的东西 ...

  3. Javascript高级编程学习笔记(4)—— JS中的数据类型(2)

    接着昨天的文章,今天这篇文章主要讲述JS中剩余的两种数据类型String,和Object String类型 对于该类型,书中给出的解释为:由0或多个16为Unicode字符组成的字符序列. 对于JS中 ...

  4. 【学习笔记】彻底理解JS中的this

    首先必须要说的是,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(这句话有些问题,后面会解释为什么会有问题,虽然 ...

  5. 前端学习笔记系列一:13new Date()的参数

    前两天发现手机页面的倒计时在Android上正常显示,在iPhone却不能显示. 后来又发现在ff和ie里也不显示.(以前只在chrome里看过,显示正常). 后来同事改了new Date()里字符串 ...

  6. JS中数组的拷贝方法

    之前在写一个vue的计算属性时,大概是这样: computed: { updateList () { let newList = this.List /*do something*/ return n ...

  7. 前端学习笔记系列一:5 在项目中引入阿里图标icon

    进入到阿里的图标库网站,里面有上百万种icon,https://www.iconfont.cn,需要注册一个帐号,然后进入到这个页面,在这里点击右下角的带加号的图标,创建一个新的项目,名称与你要使用图 ...

  8. 前端学习笔记系列一:11@vue/cli3.x中实现跨域的问题

    由于浏览器的同源访问策略,vue开发时前端服务器通常与后端api服务器并非是相同的服务器,因此需要使用一个代理服务器实现跨域访问.在@vue/cli3.x根目录下创建一个vue.config.js文件 ...

  9. 前端学习笔记系列一:10整体移动vscode代码块、VSCode 使用 stylus,配置格式化设置、在vue项目中引入bootstrap

    1.整体移动vscode代码块 凭借操作的经验我们能够轻松地知道将代码整体往右移只需选中代码按Tab键即可.其实往左移也很简单: 选中之后按下 shift+Tab键 即可. 2.VSCode 使用 s ...

随机推荐

  1. ASA设置某些log不发送到log server

    If you want to suppress a specific syslog message to be sent to syslog server, then you must enter t ...

  2. 软件版本 Alpha、Beta、Rc

    软件版本的周期 α.β.γ 表示软件测试中的三个阶段 α :第一阶段,内部测试使用 β: 第二阶段,消除了大部分不完善的地方,仍可能存在漏洞,一般提供给特定的用户使用 γ: 第三阶段,产品成熟,个别地 ...

  3. Linux 命令中 which、whereis、locate 命令的用法

    which 命令 which 命令的作用是,在 PATH 变量指定的路径中搜索可执行文件的所在位置.它一般用来确认系统中是否安装了指定的软件. (1)命令格式 which 可执行文件名称 wherei ...

  4. 如何在PHP中防止SQL注入

    使用PDO对象(对于任何数据库驱动都好用) addslashes用于单字节字符串的处理, 多字节字符用mysql_real_escape_string吧. 另外对于php手册中get_magic_qu ...

  5. SpringBoot 集成JUnit

    项目太大,不好直接测整个项目,一般都是切割成多个单元,单独测试,即单元测试. 直接在原项目上测试,会把项目改得乱七八糟的,一般是单独写测试代码. 进行单元测试,这就需要集成JUnit. (1)在pom ...

  6. Python 中的类与对象 初认识

    一:类的声明 1类的关键字: 从第一天第一个项目起我们就接触过关键字,比如False True is not None return for while elif else import等等,这是语言 ...

  7. Unity的3种消息传递方法(SendMessage等)

    为了方便多个物体间的消息传达与接收,Unity中包含了几种消息推送机制 : 分别为SendMessage.SendMessageUpwards.BroadcastMessage. 我们首先以SendM ...

  8. nginx 的precontent阶段的ngx_http_try_files_module模块与mirrors模块介绍

    指令介绍 Syntax: try_files file ... uri; try_files file ... =code; Default: — Context: server, location ...

  9. 华为事件对A股的影响思考

    美国对华为实施禁商令: 利好:自主可控-替代品 软件:国产操作系统(中国软件,浪潮软件,湘邮科技...) 芯片:国产芯片(士微兰,国民技术...) 利好:华为优势产品 5G: 利好:反制资源 稀土永磁 ...

  10. 正确使用 Android 的 Theme 和 Style

    原文:http://www.tuicool.com/articles/ZjEZFj Android 5.0 可以给一个 View 单独设置一个 theme 了,其主要用途就是用在 ToolBar 上, ...