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)


<<:  Day28 黯然消魂饭-自制蜜汁叉烧

>>:  Day-29 : Model验证

[ JS个人笔记 ] 资料型别—DAY1

资料型别 为何需判断型别,其因为电脑在执行时,需先判断资料是何种型别,才可采取运算方式。例如数字12...

DAY 01 : 让企业拥有真正属於自己的ERP

说明、缘由 投入资讯产业约17年,历经过许多的程序语言,也投入过不同的行业别,5年前开始接触Odoo...

Day 11 阿里云架设网站-DNS & 智能流量分流

云解析(Alibaba Cloud DNS) 云解析是一个在阿里上的DNS托管工具,每个云端都有他自...

Day29:翻译蒟蒻

当我们的程序可能会有其他语言使用者时,就得开始考虑国际化的问题了,专业术语叫i18n: i n t ...

[Day 15] ML 实验管理 — 翻开覆盖的陷阱卡~ 记帐小本本!

All life is an experiment. The more experiments y...