下面瞭解下什麼淺複製【拷貝】和深複製【拷貝】,經過下面的閱讀你將瞭解到:前端
一、什麼是淺複製以及使用場景
二、什麼是深複製以及使用場景
三、淺複製和深複製有哪些方式
複製代碼
1、咱們先來了解下,JavaScript基本知識,基本類型和引用類型es6
基本類型:number、string、boolean、null、undefined 後來es6又增長了一個基本類型symbol,目前基本類型爲6個
引用類型:Object、 Function、Array
複製代碼
什麼是堆【heap】和 棧 【stack】數組
棧:自動分配內存,系統自動釋放,裏面包含值類型和引用類型的地址【引用對象】
堆:動態分配內存,大小不定,系統不會自動釋放,裏面存放引用類型的值【實例對象】bash
看個例子:閉包
let a=1;
let b=a;
console.log(a)//1
console.log(b)//1
b=3;
console.log(b)//3
console.log(a)//1
複製代碼
從例子得出結論:當值賦值給變量時,解析器肯定是基本類型值仍是引用類型值。基本數據類型,是值訪問的,而且能夠操做保存在變量中的實際值。基本類型複製的時候,就是在棧中開闢一個新的存儲區域用來存儲變量。因此其中一個值變化,不會影響另外一個值.
如例子:雖然b=3發生了改變,但a輸出結果仍是1,印證上面的結論.app
let obj={name:'zjl',remark:'zjl'};
let anotherObj=obj;
anotherObj.name='lisi'
console.log(obj);//{name:'lisi',remark:'zjl'};
複製代碼
從上面的例子中,能夠看出一個值發生的改變也影響到了另外一個.
如上:obj的name由zjl變爲了lisi,這是爲何呢?函數
引用類型值【實例對象】,是存放在堆內存中的對象。與其它語言不一樣,JavaScript中,不容許直接訪問內存位置及不能直接操做內存空間。實際操做的是對象的引用【指針】,不是實際的對象。ui
補充:對象引用是棧中的地址,複製對象時,至關於在棧中開闢新的一塊區域存放這個地址(指針),這個指針指向同一塊堆內存位置。因此其中一個指針對象發生改變,另外一個也會發生改變。spa
2、下面就,常見的數組【Array】和對象【Object】,來討論下深淺複製指針
一、淺複製【拷貝】
//數組淺複製
let arr=[1,2,3,5,8];
let item=[];
for(let i in arr){
item[i]=arr[i];
}
item.push(9);
console.log('arr==>'+arr);//arr==>1,2,3,5,8
console.log('item==>'+item);//item==>1,2,3,5,8,9
//對象淺複製
let obj={name:'zjl',age:'28'};
let list={};
for(let i in obj){
list[i]=obj[i];
}
list['like']='apple';
console.dir('obj===>'+JSON.stringify(obj));
console.dir('list===>'+JSON.stringify(list));
obj===>{"name":"zjl","age":"28"}
list===>{"name":"zjl","age":"28","like":"apple"}
複製代碼
以上例子能夠看出,數組、對象實現了淺複製.可是上面的代碼只能實現一層的拷貝,沒法實現深層的拷貝,若是把上面的代碼改動下,以下:
//對象淺複製
let obj={name:'zjl',age:'28'};
let list={};
for(let i in obj){
list[i]=obj[i];
}
list['name']='lisi';
console.dir('obj===>'+JSON.stringify(obj));
console.dir('list===>'+JSON.stringify(list));
obj===>{"name":"lisi","age":"28"}
list===>{"name":"lisi","age":"28"}
複製代碼
上面的例子,能夠看出對象name被改變了.影響了原始的對象值.因引用類型爲地址傳遞,沒有開闢新的堆內存,地址指向同一塊內存位置,因此改變一個對象另外一個對象也會隨之改變。因此沒法實現深層次的複製【拷貝】.怎樣解決這個問題,咱們須要使用深拷貝【複製】來完成,繼續往下看...
二、深複製【拷貝】: 看下面的例子,遞歸實現深層複製:
var china = {
nation: '中國',
adrress: ['北京', '上海', '廣州'],
}
//深複製,要想達到深複製就須要用遞歸
function deepCopy(o, c) {
var c = c || {}
for (var i in o) {
if (typeof o[i] === 'object') { //要考慮深複製問題了
if (o[i].constructor === Array) {
//這是數組
c[i] = []
} else {
//這是對象
c[i] = {}
}
deepCopy(o[i], c[i])
} else {
c[i] = o[i]
}
}
return c
}
var result = {}
result = deepCopy(china, result);
result.nation = '美國';
console.dir(JSON.stringify(result)); //{"adrress":["北京","上海","廣州"],"nation":"美國"}
console.dir(JSON.stringify(china)); //{"adrress":["北京","上海","廣州"],"nation":"中國"}
複製代碼
從上面的例子,能夠看出已經實現了,真正的複製.下面看下圖解:
3、深複製【拷貝】方法還有不少種
深複製【拷貝】後,兩個對象,包括其內部的元素互不干擾。
一、JSON.parse(JSON.stringify())反序列化
二、JQuery自帶的,$.extend(true,{},obj);
三、loadsh.js的實現_.cloneDeep和_.clone(value, true)
複製代碼
感興趣的能夠去了解下
鑑於有朋友評論對於JSON.parse(JSON.stringify())反序列化存在侷限性,如今作以補充: 照舊,仍是先看下例子: 一、對數組對象進行深層次的拷貝
let obj={
like:'color',
list:{
item:['green','red']
}}
let deepCopy = (JSON.parse(JSON.stringify(obj)));
deepCopy.list.item[0]='yellow';
console.log(deepCopy);//{like:'color',list:{item:['yellow','red']}
console.log(obj);//{like:'color',list:{item:['green','red']}
複製代碼
二、針對undefined,function,symbol的拷貝
let obj={x: undefined, y: Object, z: Symbol("")};
console.log('序列化==>'+JSON.stringify(obj)); // 序列化==> '{}'
console.log('反序列化==>'+JSON.parse(JSON.stringify(obj))); // 反序列化==> {}
// 對應一個原型 __proto__
let arr=[undefined, Object, Symbol("")];
console.log('序列化==>'+JSON.stringify(arr));
// 序列化==> '[null,null,null]'
console.log('反序列化 arr==>'+JSON.parse(JSON.stringify(arr)));
// 反序列化 arr==> [null,null,null]
// 反序列化 對應一個原型 __proto__
複製代碼
從以上實例得出如下結論:
undefined、任意的函數以及symbol值,在序列化過程當中會被忽略(出如今非數組對象的屬性值中時)或者被轉換成 null(出如今數組中時)
複製代碼
JSON.parse(JSON.stringify())雖不能對undefined,function,symbol進行深拷貝,但使用起來簡單,能夠知足大部分的場景,具體仍是要根據須要選擇使用.
歡迎關注,【前端突擊】 獵鷹突擊,迎難而上,期待你的加入...