javascript
JavaScript之浅复制【拷贝】与深复制【拷贝】【二】
下面了解下什么淺復制【拷貝】和深復制【拷貝】,通過下面的閱讀你將了解到:
1、什么是淺復制以及使用場景 2、什么是深復制以及使用場景 3、淺復制和深復制有哪些方式 復制代碼一、我們先來了解下,JavaScript基本知識,基本類型和引用類型
基本類型:number、string、boolean、null、undefined 后來es6又增加了一個基本類型symbol,目前基本類型為6個 引用類型:Object、 Function、Array 復制代碼什么是堆【heap】和 棧 【stack】
棧:自動分配內存,系統自動釋放,里面包含值類型和引用類型的地址【引用對象】
堆:動態分配內存,大小不定,系統不會自動釋放,里面存放引用類型的值【實例對象】
看個例子:
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,印證上面的結論.
從上面的例子中,可以看出一個值發生的改變也影響到了另一個.
如上:obj的name由zjl變為了lisi,這是為什么呢?
引用類型值【實例對象】,是存放在堆內存中的對象。與其它語言不同,JavaScript中,不允許直接訪問內存位置及不能直接操作內存空間。實際操作的是對象的引用【指針】,不是實際的對象。
補充:對象引用是棧中的地址,復制對象時,相當于在棧中開辟新的一塊區域存放這個地址(指針),這個指針指向同一塊堆內存位置。所以其中一個指針對象發生改變,另一個也會發生改變。
總結下區別,兩者的主要區別就是,基本類型是值傳遞,引用類型是地址傳遞二、下面就,常見的數組【Array】和對象【Object】,來討論下深淺復制
- 淺復制:拷貝的是引用對象--拷貝的是棧中的地址【對象的引用】,棧中指針指向同一塊堆內存【實例對象】,因此修改一個引用對象,另一個引用對象也隨之修改.就像你拿了一把鑰匙打開一扇門,別人拿到你這把鑰匙也能打開這扇門,并且從房間拿走了東西,你拿到這把鑰匙開門后發現東西少了,簡言之也就是共享了同一個房間,JavaScript中就是同一塊堆內存.
- 深復制:拷貝的是實例對象。因實例對象放在堆內存中的,要想實現深拷貝,必須重開辟一塊堆內存,新創建原對象的實例,并保證不同的對象引用。使原對象與新建對象完全隔離互不影響。
1、淺復制【拷貝】
//數組淺復制 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被改變了.影響了原始的對象值.因引用類型為地址傳遞,沒有開辟新的堆內存,地址指向同一塊內存位置,所以改變一個對象另一個對象也會隨之改變。所以無法實現深層次的復制【拷貝】.怎樣解決這個問題,我們需要使用深拷貝【復制】來完成,繼續往下看...
2、深復制【拷貝】: 看下面的例子,遞歸實現深層復制:
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":"中國"} 復制代碼從上面的例子,可以看出已經實現了,真正的復制.下面看下圖解:
三、深復制【拷貝】方法還有很多種
深復制【拷貝】后,兩個對象,包括其內部的元素互不干擾。
1、JSON.parse(JSON.stringify())反序列化 2、JQuery自帶的,$.extend(true,{},obj); 3、loadsh.js的實現_.cloneDeep和_.clone(value, true) 復制代碼感興趣的可以去了解下
鑒于有朋友評論對于JSON.parse(JSON.stringify())反序列化存在局限性,現在做以補充: 照舊,還是先看下例子: 1、對數組對象進行深層次的拷貝
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']} 復制代碼2、針對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進行深拷貝,但使用起來簡單,可以滿足大部分的場景,具體還是要根據需要選擇使用.
JavaScript之閉包【三】
總結
以上是生活随笔為你收集整理的JavaScript之浅复制【拷贝】与深复制【拷贝】【二】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linu20180415三周第三次课(4
- 下一篇: 为Pdf批量添加水印