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