前言
因为公司前端资料已经处理成单层结构,所以都没注意到浅拷贝、深拷贝的实际差别。
在读完高手文章后,才发现和自己想的不一样。
也顺手将文章重点整理,分享给大家,别枉费自己写这么多Code。
重点
基本型别 ⇒ 传值。string
、number
、boolean
、null
、undefined
物件型别 ⇒ 传址浅拷贝 Shallow Copy ⇒ 物件若有多层,第一层之下的物件会以传址拷贝。物件的浅拷贝方法Object.assign()
...
阵列的浅拷贝方法array.slice()
array.concat()
array.map()
array.filter()
forEach
+push()
...
深拷贝 Deep Copy ⇒ 返回一个全新的物件且不与被複製物件有关係。物件、阵列深拷贝方式JSON.parse(JSON.stringify())
第三方函示库,Lodash ⇒ _.cloneDeep()
...等等传值
var a = 10;var b = 10;console.log(a === b); // true
a
、b
皆为基本型别,故互相比较时回传true
传址
var obj1 = { a : 1 };var obj2 = { a : 1 };console.log(obj1 === obj2); // false
虽然物件内容相等,但是物件的记忆体位置却不同,故比较结果为false
。
不同的例子
var obj1 = { a : 1 };var obj2 = obj1;obj1.a = 0;console.log(obj1.a); // 0console.log(obj2.a); // 0obj2.a = 2;console.log(obj1.a); // 2console.log(obj2.a); // 2console.log(obj1 === obj2); // true
当我们修改任何一边的属性时,会一起变动,这是因为物件透过传址的方式指派,所以两个物件会指向同一个记忆体位置,实际上也意味着并未产生新的物件。
但若是这样呢?
var obj1 = { a : 1 };var obj2 = obj1;obj1.a = 0;console.log(obj1.a); // 0console.log(obj2.a); // 0obj2.a = 2;console.log(obj1.a); // 2console.log(obj2.a); // 2obj1 = {}; // 指派新的物件console.log(obj1 === obj2); // false
当obj1
被指向新的记忆体位置,但是obj2
依然保持原本的记忆体位置,因此这时obj1
、obj2
彼此就毫无关係了。
複製物件
複製物件可以分为两种
浅拷贝 Shallow Copy深拷贝 Deep Copy浅拷贝
物件的浅拷贝
Object.assign()
展开运算子...
Object.assign(target, source)
能複製一个或多个物件自身所有可数的属性到另一个目标物件。
将原本的obj
内容複製到另一个空物件
var obj = { a : 1, b : 2 };var obj2 = Object.assign({}, obj);console.log(obj2); // { a : 1, b : 2 }console.log(obj == obj2); // false
展开运算子
var obj = { a : 1, b : 2 };var obj2 = { ...obj1 };console.log(obj2); // { a : 1, b : 2 }console.log(obj === obj2); // false
阵列的浅拷贝
array.slice()
array.concat()
array.map()
array.filter()
forEach
+push()
...
slice()
原本是用在分割阵列,但用参数为0或不传入的话,相当于浅拷贝
var arr = [1, 2, 3, 4];var arr2 = arr;console.log(arr === arr2); // truevar arr3 = arr.slice(0); console.log(arr === arr3); // falseconsole.log(arr3); // [1, 2, 3, 4]
concat()
原本是用在组合阵列,但可以使用空阵列合併,也相当于浅拷贝
var arr = [1, 2, 3, 4];var arr2 = [].concat(arr);console.log(arr === arr2); // falseconsoloe.log(arr2); // [1, 2, 3, 4]
map()
是将执行结果会存至新阵列,若回传原本的元素,也相于浅拷贝
var arr = [1, 2, 3, 4];var arr2 = arr.map(x => x);console.log(arr === arr2); // falseconsole.log(arr2); // [1, 2, 3, 4]
filter
是将符合条件的值存至新阵列,若条件皆为true
,也相当于浅拷贝
var arr = [1, 2, 3, 4];var arr2 = arr.filter(x => { return true; } );console.log(arr === arr2); // falseconsole.log(arr2); // [1, 2, 3, 4]
...
也可用于阵列,效果也是浅拷贝
var arr = [1, 2, 3, 4];var arr2 = [ ...arr ];console.log(arr === arr2); // falseconsole.log(arr2); // [1, 2, 3, 4]
至于为什么叫浅拷贝,可以看以下程式码
var obj1 = { foo : 10, bar : { baz : 20, },};var obj2 = { ...obj1 };console.log(obj1 === obj2); // falseconsole.log(obj1.bar === obj2.bar); // true
浅拷贝后,obj1
、obj2
已经是两个不同的物件,但是第二层的物件却是相同的记忆体位置。
再看看阵列
var arr = [{a : 1}, {b : 2}];var arr2 = [...arr];console.log(arr === arr2); // falseconsole.log(arr[0] === arr2[0]); // true
浅拷贝后,arr
、arr2
已经是不同的阵列,但是里面的物件却是同一个。
上述只是有容器不同,若有巢状或多维的状况,仍然是传址。因为深层的物件或阵列还是传址,不会完全複製一份,所以称为『浅拷贝』。
深拷贝
深拷贝是完全複製一份新的,不会共用记忆体位置。
Json.parse(Json.stringify())
第三方函示库,Lodash ⇒ _.cloneDeep()
...等等利用JSON
var obj1 = {a : 1,b : {val: 5}};var obj2 = JSON.parse(JSON.stringify(obj1));console.log(obj1 === obj2); // falseconsole.log(obj1.b === obj2.b); // false
但JSON的转法须注意
函式、undefined
、Symbol
会被忽略转换NaN
、Infinity
会被转换成 null
var obj1 = { a: function() {}, b: undefined, c: Symbol(''), d: NaN, e: Infinity, f: -Infinity,};var obj2 = JSON.parse(JSON.stringify(obj1));console.log(obj2); // {d: null, e: null, f: null}
参考
参考文章