JavaScript深拷貝與淺拷貝原理深入探究
一、JS中數(shù)據(jù)的存儲形式-堆棧
我們先簡單理解一下堆棧分別是啥:
什么是棧:計(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脑恚覀冊诙x原始類型數(shù)據(jù)的時(shí)候都會開辟一個(gè)棧的空間,當(dāng)聲明一個(gè)基本變量時(shí),它就會被存儲到棧內(nèi)存中,而當(dāng)其發(fā)生復(fù)制時(shí),會把對應(yīng)內(nèi)存中的數(shù)據(jù)復(fù)制一份到新內(nèi)存中,所以這兩個(gè)變量之間沒有什么聯(lián)系。如果我們對引用類型進(jìn)行復(fù)制,我們只是將地址復(fù)制了一遍,原變量和復(fù)制的新變量都是指向同一個(gè)地址,這就說明對新變量進(jìn)行修改時(shí)就會影響到原變量的值。
二、深淺拷貝的三種方式
- 遍歷賦值
- Object.create()
- JSON.parse()和JSON.stringify()
深拷貝與淺拷貝,簡單點(diǎn)來說:
就是假設(shè)B賦值了A,當(dāng)修改A時(shí),看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);在這里我們定義了一個(gè)對象 obj,為了實(shí)現(xiàn)能夠復(fù)制出另一個(gè)對象,我們再定義一個(gè)clone方法。然后調(diào)用這個(gè)方法,輸出原來的對象和復(fù)制后的對象,看看是否相同:

在控制臺下輸出的兩個(gè)結(jié)果沒有任何差異,那他們兩個(gè)是否真的完全一樣呢?
他們兩個(gè)是有不同的,因?yàn)檫@里的拷貝屬于淺拷貝,我們根據(jù)淺拷貝的定義,如果在上例中,我們改變了objcopy,那么obj也發(fā)生了變化的話那他就是一個(gè)淺拷貝。
我們下面來驗(yàn)證一下,改變一下objcopy.b.a的值,看看 obj 里會不會發(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都會變成什么。
Object.create()
這種方法只需要行代碼:
var objcopy = Object.create(obj)
obj在復(fù)制的時(shí)候,它會被當(dāng)前對象復(fù)制到原型__proto__上,并不是復(fù)制到當(dāng)前的對象上。我們再次改變一下objcopy.b.a的值,看看 obj 里會不會發(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都會被賦值為空對象,就會對已經(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)深拷貝
這是我們在工作中最常見的一種方式
var objcopy = JSON.parse(JSON.stringify(obj))
我們對初始化的對象先通過JSON.stringify轉(zhuǎn)換為字符串,再通過JSON.parse轉(zhuǎn)回對象類型。
這樣為什么能實(shí)現(xiàn)深拷貝的效果呢?因?yà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)文章
基于Bootstrap實(shí)現(xiàn)下拉菜單項(xiàng)和表單導(dǎo)航條(兩個(gè)菜單項(xiàng),一個(gè)下拉菜單和登錄表單導(dǎo)航條)
這篇文章主要介紹了基于Bootstrap實(shí)現(xiàn)下拉菜單項(xiàng)和表單導(dǎo)航條(兩個(gè)菜單項(xiàng),一個(gè)下拉菜單和登錄表單導(dǎo)航條)的相關(guān)資料,需要的朋友可以參考下2016-07-07
js正則校驗(yàn)特殊的不可見字符的具體實(shí)現(xiàn)
用戶可能從Excel或者其他地方直接復(fù)制粘貼,這時(shí)候提交到后端會導(dǎo)致獲取的用戶輸入中包含一些特殊的不可見字符,本文主要介紹了js正則校驗(yàn)特殊的不可見字符的具體實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06
JavaScript encodeURI 和encodeURIComponent
encodeURI和encodeURIComponet函數(shù)都是javascript中用來對URI進(jìn)行編碼,將相關(guān)參數(shù)轉(zhuǎn)換成UTF-8編碼格式的數(shù)據(jù)。URI在進(jìn)行定位跳轉(zhuǎn)時(shí),參數(shù)里面的中文、日文等非ASCII編碼都會進(jìn)行編碼轉(zhuǎn)換2015-12-12
JS實(shí)現(xiàn)的簡單下拉框聯(lián)動功能示例
這篇文章主要介紹了JS實(shí)現(xiàn)的簡單下拉框聯(lián)動功能,涉及javascript事件響應(yīng)及頁面元素屬性動態(tài)修改相關(guān)操作技巧,需要的朋友可以參考下2018-05-05
JS生成某個(gè)范圍的隨機(jī)數(shù)【四種情況詳解】
下面小編就為大家?guī)硪黄狫S生成某個(gè)范圍的隨機(jī)數(shù)【四種情況詳解】。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考,一起跟隨小編過來看看吧2016-04-04

