在ES6中新增了擴展運算符可以對數組和對象進行操作。有時候會遇到數組和對象的拷貝,可能會用到擴展運算符。那么這個擴展運算符到底是深拷貝還是淺拷貝呢?
一.、使用擴展運算符拷貝
首先是下面的代碼。
let a = [1,2,3]; let b = [...a];
a == b // false
結果是false,這是很容易知道的,畢竟這個賦值操作符是有區別的。接下來將數組的值進行改變,又會怎樣呢;
let a = [1,2,3]; let b = [...a]; a[0] = 11; console.log(a); // [ 11, 2, 3 ] console.log(b); // [ 1, 2, 3 ]
發現a的值發生改變之后b的值並沒有發生改變。所以就是深拷貝了嗎?別急,接下來將數組中的元素設為引用類型。
let a = [1,2,[1,2,3]]; let b = [...a]; a[2][1] = 11; console.log(a); // [ 1, 2, [ 1, 11, 3 ] ] console.log(b); // [ 1, 2, [ 1, 11, 3 ] ]
console.log(a[2] === b[2]); // true
這次的結果就有意思了,如果改變數組中的引用類型的元素中的值,此時a和b的值都會改變,並且a和b中的引用類型全等,也就是說地址是相同的。那么為什么是這樣的呢?
二.、原因
首先此分析僅為本人目前的認知。
對於數組中的擴展運算符只是一個淺拷貝,僅對引用類型數據的第一層進行了拷貝,而倘若再深的層次就不會進行拷貝。
另外對象的擴展運算符和數組是一樣的。
let a = { name : "Jyy", msg : { age : 29 } } let b = {...a}; console.log(a == b); // false console.log(a.msg == b.msg); // true; a.msg = { age : "28" } console.log(a); // { name: 'Jyy', msg: { age: '28' } } console.log(b); // { name: 'Jyy', msg: { age: 29 } }
三、深拷貝和淺拷貝的方法
1.淺拷貝方法
上面的例子已經看出來es6中的擴展運算符僅僅對引用類型進行了第一層的拷貝。除了es6的擴展運算符還有其他方法
對象:
使用Object.assign()
Object.assign()用於對象的合並,如果第一個參數為{},則可對后面的對象參數進行拷貝
let a = { name : "Jyy", msg : { age : 29 } } let b = Object.assign({},a); console.log(a == b); // false console.log(a.msg == b.msg); // true; a.msg = { age : "28" } console.log(a); // { name: 'Jyy', msg: { age: '28' } } console.log(b); // { name: 'Jyy', msg: { age: 29 } }
數組:
數組的淺拷貝的方法很多
a.使用slice()
slice可以截取數組中部分的元素,若參數為空,則可對數組進行淺拷貝
let a = [1,2,[1,2,3]]; let b = a.slice(); console.log(a == b); // false a[2][1] = 11; a[0] = 11; console.log(a); // [ 11, 2, [ 1, 11, 3 ] ] console.log(b); // [ 1, 2, [ 1, 11, 3 ] ] console.log(a[2] == b[2]); // true
b.使用concat()
concat可以對數組進行合並,若參數為空,亦可對數組進行淺拷貝
let a = [1,2,[1,2,3]]; let b = a.concat(); console.log(a == b); // false a[2][1] = 11; a[0] = 11; console.log(a); // [ 11, 2, [ 1, 11, 3 ] ] console.log(b); // [ 1, 2, [ 1, 11, 3 ] ] console.log(a[2] == b[2]); // true
c.使用Array.from()
let a = [1,2,[1,2,3]]; let b = Array.from(a); console.log(a == b); // false a[2][1] = 11; a[0] = 11; console.log(a); // [ 11, 2, [ 1, 11, 3 ] ] console.log(b); // [ 1, 2, [ 1, 11, 3 ] ] console.log(a[2] == b[2]); // true
2.深拷貝
對於深拷貝,數組和對象的方法是一致的
a.遞歸方法,就是用for循環一層一層的進行拷貝,具體代碼就不寫了
b.JSON.parse()
這個方法通常用於調用接口傳參或者是返回的字符串數據轉成對象。
let a = { name : "JYY", age : "25", msg : { addr : "hebei" } } b = JSON.parse(JSON.stringify(a)); console.log(a == b); // false console.log(a.msg == b.msg); // false a.msg.addr = "chengde"; console.log(a); // { name: 'JYY', age: '25', msg: { addr: 'chengde' } } console.log(b); // { name: 'JYY', age: '25', msg: { addr: 'hebei' } }
這個方法的弊端就是undefined、function、symbol 會在轉換過程中被忽略
let a = { name : "JYY", age : "25", msg : { addr : "hebei" }, speek : function(){ console.log(this.name); } } b = JSON.parse(JSON.stringify(a)); console.log(a); // { name: 'JYY', height: Symbol(jyy), age: undefined, msg: { addr: 'chengde' },speek: [Function: speek] } console.log(b); // { name: 'JYY', msg: { addr: 'hebei' } }
c.使用第三方插件
比如lodash的深拷貝
const _ = require("lodash");
let syb = Symbol('jyy');
let a = {
name : "JYY",
height: syb,
age : undefined,
msg : {
addr : "hebei"
},
speek : function(){
console.log(this.name);
}
}
let b = _.cloneDeep(a);
console.log(a == b); // false
console.log(a.msg == b.msg); // false
console.log(a); // { name: 'JYY', height: Symbol(jyy), age: undefined, msg: { addr: 'chengde' },speek: [Function: speek] }
console.log(b); // { name: 'JYY', height: Symbol(jyy), age: undefined, msg: { addr: 'chengde' },speek: [Function: speek] }
