前阵子刚写完 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.stringify
和 JSON.parse
,JSON.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.stringify
和 JSON.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)