JavaScript. 浅拷贝与深拷贝

前阵子刚写完 JavaScript,差不多可以开始进行 Vue 的时候,突然想起在 Vue 开发的时候总是搞不清楚什么是浅拷贝 ( Shallow Copy ) ,什么是深拷贝 ( Deep Copy ),也因此在练习的时候很常出现令自己感到问号的错误,那么今天也算是为了让 Vue 的开发更顺畅,来研究一下这两种写法。

浅拷贝 ( Shallow Copy )

只能对第一层进行浅层複製,如果有第二层结构,还是会依据参考特性作处理;也就是说,在第二层开始他们的记忆体位置还是一样的,因此依然会覆盖掉原物件。

浅层拷贝分成两种方法:

Object.assign

Object.assign() 方法,被用来複製一个或多个物件,回传的值则为複製的该物件。

Object.assign(target, ...sources)

target / 目标物件
sources / 複製来源物件

範例:

let ary = ['齐天大圣孙悟空', '猪八戒', '沙悟净', '唐僧'];let obj = {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'};let aryNew = Object.assign([], ary);aryNew[1] = '紫霞仙子';console.log(aryNew); // ['齐天大圣孙悟空', '紫霞仙子', '沙悟净', '唐僧'] 新阵列资料已更改console.log(ary); // ['齐天大圣孙悟空', '猪八戒', '沙悟净', '唐僧'] 原阵列不受影响let objNew = Object.assign({}, obj);objNew.woman1 = '铁扇公主';// {woman1: '铁扇公主', woman2: '紫霞仙子', man: '牛魔王'} 新物件资料更改console.log(objNew); // {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'} 原物件没有被更动console.log(obj); 

但目前我们做的都只是第一层的拷贝,底下我们来试试看在更深入的拷贝,结果会是如何。

let ary = [{woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'}];let aryNew = Object.assign([], ary);aryNew[0].woman1 = '铁扇公主';console.log(ary);// {woman1: '铁扇公主', woman2: '紫霞仙子', man: '牛魔王'} 

可以发现到了第二层,会直接更动原物件。

展开运算子

运算子展开的浅拷贝方式,是 ES6 新增的特性,主要是把一个阵列展开变成个别值,再放入指定的物件或阵列。

範例:

let ary = ['齐天大圣孙悟空', '猪八戒', '沙悟净', '唐僧'];let obj = {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'};let aryNew = [...ary];aryNew[1] = '紫霞仙子';console.log(aryNew); // ['齐天大圣孙悟空', '紫霞仙子', '沙悟净', '唐僧'] 新阵列资料已更改console.log(ary); // ['齐天大圣孙悟空', '猪八戒', '沙悟净', '唐僧'] 原阵列不受影响let objNew = {...obj};objNew.woman1 = '铁扇公主';// {woman1: '铁扇公主', woman2: '紫霞仙子', man: '牛魔王'} 新物件资料更改console.log(objNew); // {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'} 原物件没有被更动console.log(obj); 

这边到目前为止跟上面的範例是差不多的,不一样的地方只在于展开这个动作,而在第二层也同样会出现改变原物件的情况。


深拷贝 ( Deep Copy )

能深度複製同样的物件,但记忆体位置不同,两个为独立的记忆体空间,因此不会互相影响。

深拷贝也有两种方式:

JSON.stringify,主要利用 JSON.stringify 把物件转成字串,再用 JSON.parse 把字串转为物件。

这里稍微说明一下 JSON.stringifyJSON.parseJSON.stringify 为物件变 JSON 字串,JSON.parse 则是将 JSON 字串转物件。

JSON.stringify 範例:

let me = {name: 'Jemma'};console.log(JSON.stringify(me)); // {"name":"Jemma"}

JSON.parse 範例:

let myName = JSON.parse('{"name":"Jemma"}');console.log(myName); // {name: 'Jemma'}

稍微解释完 JSON.stringifyJSON.parse,回归深拷贝的话题,刚刚说到深拷贝可以使用JSON.stringify 方法,我们一样直接来看範例:

let ary = [{woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'}];let aryNew = JSON.parse(JSON.stringify(ary));aryNew[0].woman1 = '铁扇公主';console.log(ary);// {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'} 原资料没有被更动console.log(aryNew);// {woman1: '铁扇公主', woman2: '紫霞仙子', man: '牛魔王'}

可以看到这个範例,其实是拿示範浅拷贝第二层资料是否改变的那个範例,来做深拷贝示範,在浅拷贝的时候原资料确实被改变了,但在深拷贝的时候可以看到,aryNew 是一笔新的资料,因此不影响原本的 ary。

$.extend

在 jQuery 也提供一个深拷贝的方法,利用 $.extend 指定强制深拷贝,这里也稍微解释一下 $.extend

$.extend / 将两个或更多对象的内容合併到第一个对象。

语法:

jQuery.extend( [deep ], target, object1 [, objectN ] )// deep 深拷贝

回到利用 $.extend 做深拷贝的範例,这里我一直无法 console 出来满满问号脸,为了避免花太多时间卡在这里,因此暂时先借用网路大神的範例:

let data = [  {    name: 'Eric',    weight: 60,  },];let dataCP = $.extend(true, [], data);// 操作第一层 : 不影响原物件dataCP.push({  name: 'Alice',  weight: 50,});// 操作第二层 : 不影响原物件dataCP[0].name = 'Emma';console.log(data); // [ { name: 'Eric', weight: 60 } ]console.log(dataCP); // [ { name: 'Emma', weight: 60 }, { name: 'Alice', weight: 50 } ]

今天的文章就先分享到这,日后有研究出个什么再补上了。

参考资料:

JavaScript 浅拷贝 (Shallow Copy) 与深拷贝 (Deep Copy)


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章