JavaScript深拷貝與淺拷貝原理深入探究
一、JS中數(shù)據(jù)的存儲(chǔ)形式-堆棧
我們先簡(jiǎn)單理解一下堆棧分別是啥:
什么是棧:計(jì)算機(jī)為原始類型開辟的一塊內(nèi)存空間 string number ...
什么是堆:計(jì)算機(jī)為引用類型開辟的一塊內(nèi)存空間 object
我們分別分析下面兩段代碼:
var a = 'jack' var b = a b = 'andy' console.log(a,b);//jack andy
var c = {key : 1} var d = c d.key = 2 console.log(c,d);//{ key: 2 } { key: 2 }
看完之后我們可能有這么一個(gè)疑問,第一段代碼很好理解,但是第二段代碼里改變的明明是d中的key,為啥c里的key也改變了呢?
這里就是因?yàn)槎迅鷹5脑?,我們?cè)诙x原始類型數(shù)據(jù)的時(shí)候都會(huì)開辟一個(gè)棧的空間,當(dāng)聲明一個(gè)基本變量時(shí),它就會(huì)被存儲(chǔ)到棧內(nèi)存中,而當(dāng)其發(fā)生復(fù)制時(shí),會(huì)把對(duì)應(yīng)內(nèi)存中的數(shù)據(jù)復(fù)制一份到新內(nèi)存中,所以這兩個(gè)變量之間沒有什么聯(lián)系。如果我們對(duì)引用類型進(jìn)行復(fù)制,我們只是將地址復(fù)制了一遍,原變量和復(fù)制的新變量都是指向同一個(gè)地址,這就說明對(duì)新變量進(jìn)行修改時(shí)就會(huì)影響到原變量的值。
二、深淺拷貝的三種方式
- 遍歷賦值
- Object.create()
- JSON.parse()和JSON.stringify()
深拷貝與淺拷貝,簡(jiǎn)單點(diǎn)來說:
就是假設(shè)B賦值了A,當(dāng)修改A時(shí),看B是否會(huì)發(fā)生變化,如果B也跟著變了,說明這是淺拷貝。如果B沒變,那就是深拷貝。
遍歷賦值
下面我們看一段代碼:
var obj = { a:'hello', b:{ a:'world', b:111 }, c:[11,'jack','abdy'] } function clone(obj) { var objnew = {} for(var i in obj) { objnew[i] = obj[i] } return objnew } var objcopy = clone(obj) console.log(obj); console.log(objcopy);
在這里我們定義了一個(gè)對(duì)象 obj,為了實(shí)現(xiàn)能夠復(fù)制出另一個(gè)對(duì)象,我們?cè)俣x一個(gè)clone方法。然后調(diào)用這個(gè)方法,輸出原來的對(duì)象和復(fù)制后的對(duì)象,看看是否相同:
在控制臺(tái)下輸出的兩個(gè)結(jié)果沒有任何差異,那他們兩個(gè)是否真的完全一樣呢?
他們兩個(gè)是有不同的,因?yàn)檫@里的拷貝屬于淺拷貝,我們根據(jù)淺拷貝的定義,如果在上例中,我們改變了objcopy,那么obj也發(fā)生了變化的話那他就是一個(gè)淺拷貝。
我們下面來驗(yàn)證一下,改變一下objcopy.b.a的值,看看 obj 里會(huì)不會(huì)發(fā)生相應(yīng)的變化:
將objcopy.b.a賦值為字符串hhhh后,obj.b.a也變?yōu)榱薶hhh,這就說明我們最開始的拷貝屬于淺拷貝。因?yàn)閛bj.b他是一個(gè)引用類型,所以objcopy.b和obj.b指向的都是同一個(gè)地址,這樣不管objcopy.b.a改成什么,obj.b.a都會(huì)變成什么。
Object.create()
這種方法只需要行代碼:
var objcopy = Object.create(obj)
obj在復(fù)制的時(shí)候,它會(huì)被當(dāng)前對(duì)象復(fù)制到原型__proto__上,并不是復(fù)制到當(dāng)前的對(duì)象上。我們?cè)俅胃淖円幌耾bjcopy.b.a的值,看看 obj 里會(huì)不會(huì)發(fā)生相應(yīng)的變化:
所以他也是淺拷貝
遍歷賦值實(shí)現(xiàn)深拷貝
這是深拷貝的克隆函數(shù):
function deepclone(startobj,endobj) { var obj = endobj || {} for(var i in startobj) { if(typeof startobj[i] === 'object') { obj[i] = startobj[i].constructor === Array ? [] : {} deepclone(startobj[i],obj[i]) }else { obj[i] = startobj[i] } } return obj }
值得注意的一點(diǎn)是,在遞歸調(diào)用的時(shí)候,需要把當(dāng)前處理的 obj[i] 給傳回去,否則的話 每次遞歸obj都會(huì)被賦值為空對(duì)象,就會(huì)對(duì)已經(jīng)克隆好的數(shù)據(jù)產(chǎn)生影響。
我們來驗(yàn)證一下是否是深拷貝:
var objcopy = deepclone(obj) objcopy.b.a = 'hhhh' console.log(obj); console.log(objcopy);
運(yùn)行結(jié)果:
這就說明我們的深拷貝已經(jīng)實(shí)現(xiàn)了。
通過JSON.parse()和JSON.stringify()實(shí)現(xiàn)深拷貝
這是我們?cè)诠ぷ髦凶畛R姷囊环N方式
var objcopy = JSON.parse(JSON.stringify(obj))
我們對(duì)初始化的對(duì)象先通過JSON.stringify轉(zhuǎn)換為字符串,再通過JSON.parse轉(zhuǎn)回對(duì)象類型。
這樣為什么能實(shí)現(xiàn)深拷貝的效果呢?因?yàn)槲覀兺ㄟ^ JSON.stringify 轉(zhuǎn)成 string 類型后,他就存儲(chǔ)在棧里,這樣就不會(huì)出現(xiàn)地址值上的誤會(huì)了,再通過JSON.parse就可以再轉(zhuǎn)換為object類型。
到此這篇關(guān)于JavaScript深拷貝與淺拷貝原理深入探究的文章就介紹到這了,更多相關(guān)JS深拷貝與淺拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實(shí)現(xiàn)時(shí)間軸自動(dòng)播放
這篇文章主要為大家詳細(xì)介紹了JS實(shí)現(xiàn)時(shí)間軸自動(dòng)播放,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08JavaScript 無縫上下左右滾動(dòng)加定高定寬停頓效果(兼容ie/ff)
JavaScript 指定寬度高度的無間斷滾動(dòng)實(shí)現(xiàn)代碼,這樣的效果適合作為焦點(diǎn)新聞的輪播顯示。2010-03-03js與jquery實(shí)時(shí)監(jiān)聽輸入框值的oninput與onpropertychange方法
這篇文章主要介紹了js與jquery實(shí)時(shí)監(jiān)聽輸入框值的oninput與onpropertychange方法,實(shí)例分析了oninput與onpropertychange實(shí)現(xiàn)下拉框里自動(dòng)匹配關(guān)鍵字實(shí)時(shí)監(jiān)聽文本框value值變化的功能,需要的朋友可以參考下2015-02-02javascript寫的簡(jiǎn)單的計(jì)算器,內(nèi)容很多,方法實(shí)用,推薦
最近用javascript寫了一個(gè)簡(jiǎn)單的計(jì)算器,自己測(cè)試感覺還好,代碼都給了注釋,非常不錯(cuò),推薦大家學(xué)習(xí)。2011-12-12JavaScript使用WebSocket實(shí)現(xiàn)實(shí)時(shí)通信的技術(shù)詳解
WebSocket作為一種高效的通信協(xié)議,為開發(fā)者提供了一種在客戶端和服務(wù)器之間進(jìn)行全雙工通信的方法,本文將深入探討WebSocket技術(shù),并提供實(shí)戰(zhàn)代碼示例2024-04-04javascript通過class來獲取元素實(shí)現(xiàn)代碼
javascript獲取元素有很多的方法,本文簡(jiǎn)單的介紹下通過class獲取元素的實(shí)現(xiàn)代碼,感興趣的朋友可以參考下,希望本文知識(shí)點(diǎn)可以幫助到你2013-02-02