需要懂的基礎(chǔ)知識(shí)
堆棧
stack 棧 育拨,由自動(dòng)分配的內(nèi)存空間谨履,由系統(tǒng)自動(dòng)釋放
heap 堆 ,動(dòng)態(tài)分配的內(nèi)存至朗,堆存數(shù)據(jù)隨機(jī)存放 將指針指向棧內(nèi)存
js數(shù)據(jù)類型
基本數(shù)據(jù):變量與值都是在棧中
引用數(shù)據(jù)屉符,就是變量存在棧,但是值是對(duì)象,這個(gè)是保存在堆內(nèi)存中的
基本數(shù)據(jù)類型:undefined矗钟、null唆香、boolean、number吨艇、string躬它,這些類型按值訪問(wèn),可以操作保存在變量中的實(shí)際值
引用數(shù)據(jù)類型:object东涡、array冯吓、function、date對(duì)象等疮跑,大概就是一個(gè)對(duì)象可以由多個(gè)值組成组贺。js不允許直接訪問(wèn)內(nèi)存的位置,所以我們操作對(duì)象的時(shí)候祖娘,只是在操作對(duì)象的引用失尖,而不是實(shí)際的對(duì)象。
深淺拷貝原理理解
深淺拷貝理解渐苏,拷貝就是復(fù)制掀潮,js對(duì)引用類型數(shù)據(jù)的數(shù)據(jù)拷貝,淺拷貝.由于引用類型數(shù)據(jù)是存在堆內(nèi)存中琼富,堆內(nèi)存中存放的是引用類型的值仪吧,同時(shí)引用數(shù)據(jù)有會(huì)指針指向棧內(nèi)存,淺拷貝的指針指向棧內(nèi)存是一致的鞠眉,所以一個(gè)改變另一個(gè)也會(huì)受影響薯鼠。深拷貝是在堆內(nèi)存開(kāi)啟新的空間存放數(shù)據(jù)。
簡(jiǎn)單理解就是:如果拷貝產(chǎn)生新的數(shù)據(jù)就是深拷貝械蹋,如果只是數(shù)據(jù)的引用人断,就是淺拷貝
var a = [1,2,3]
var b = a //這是傳址,傳給變量的數(shù)據(jù)是引用數(shù)據(jù)朝蜘,就會(huì)把數(shù)據(jù)存在堆中.所以引用數(shù)據(jù)恶迈,就是變量名在棧,值在堆
var c = a[1] // 這是傳值谱醇,直接把值賦給變量暇仲, 這個(gè)c就是基本數(shù)據(jù)類型,存在棧中,改變棧的數(shù)據(jù)不會(huì)影響堆的數(shù)據(jù)
此時(shí):改變b的值副渴,b[1] = 3, 此時(shí)a[1]也會(huì)變成3
但是你改變c = 7奈附, a[1] 依舊是之前的值,不會(huì)變成7
理解: a是引用數(shù)據(jù)煮剧,b=a 是把棧中的地址傳給b斥滤,但是堆內(nèi)存中存放數(shù)據(jù)的對(duì)象還是一致将鸵,相當(dāng)于只是增加一個(gè)棧內(nèi)存指針指向同一個(gè)堆內(nèi)存。
所以 修改b會(huì)根據(jù)內(nèi)存修改到a的堆中佑颇,所以b改=>a變顶掉。
而c是獲得堆內(nèi)存中的一個(gè)值,并保存在棧中挑胸, c的修改是棧修改痒筒,無(wú)法保存到堆,所以a不會(huì)受影響茬贵。
注意:基本數(shù)據(jù)只是拷貝副本在棧中簿透,與深淺拷貝無(wú)關(guān)(深淺拷貝只和引用數(shù)據(jù)有關(guān))
**** 題外話:js垃圾回收機(jī)制 就是處理內(nèi)存,椊庠澹基本是用完就回收老充,堆看變量有沒(méi)有全部調(diào)用完,才回收,我也是一知半解具體要看具體資料螟左。
可參考阮一峰老師的詳細(xì)講解看看:http://www.ruanyifeng.com/blog/2017/04/memory-leak.html
代碼實(shí)操(實(shí)現(xiàn)深淺拷貝)
var a ={
age:'123',
hobby:['basketball','singing','watch movie']
}
var aCopy = {
}
目的:實(shí)現(xiàn)淺蚂维、深拷貝a 到aCopy,利用遞歸
### 我們用a形參代替被拷貝對(duì)象路狮,b形參代替目標(biāo)拷貝對(duì)象
### 淺拷貝
### 這里由于我們不是遍歷數(shù)組,遍歷對(duì)象蔚约,所以用for in
function shallowCopy(a,b){
for(var attr in a ){
b[attr] = a[attr]
}
}
shallowCopy(a,aCopy);//實(shí)現(xiàn)淺拷貝
// 這個(gè)時(shí)候只需要?jiǎng)h除奄妨、修改a 的基本數(shù)據(jù)age、引用數(shù)據(jù)favorite 就可以看見(jiàn) aCopy的基本數(shù)據(jù)不變苹祟,但是引用數(shù)據(jù)會(huì)變
### 深拷貝(遞歸:簡(jiǎn)單說(shuō)自己調(diào)用自己砸抛,利用棧的壓棧出棧,先進(jìn)后出)
這個(gè)棧流程圖利用下圖方法树枫,對(duì)此講解
function a(){
b();
console.log('a');
return
}
function b(){
console.log('b');
return
}
function c(){
a();
b();
console.log('c')
}
c()
- 流程圖的方塊代表?xiàng)?/li>
graph LR
壓棧過(guò)程第一次
D[空] --> A
A[c] --> B[a,c]
B --> C[b,a,c]
出棧過(guò)程第一次
A1[b,a,c] --> B1[a,c]
B1 --> C1[c]
壓棧過(guò)程第二次
A2[c] --> B2[b,c]
出棧過(guò)程第二次
A3[b,c] --> B3[c]
出棧過(guò)程第三次
A4[c] --> B4[空]
- 了解需要懂的原理開(kāi)始寫(xiě)代碼
function deepCopy(a,b){
for(var attr in a){
var item = a[attr]; //取出被拷貝對(duì)象的屬性數(shù)據(jù)直焙,進(jìn)行判斷是否是引用數(shù)據(jù)進(jìn)行拷貝
if(item instanceof Array){
b[attr] = [];//這個(gè)空數(shù)組 就是我們暫存數(shù)據(jù)的地方,開(kāi)辟新堆存數(shù)據(jù)砂轻,實(shí)現(xiàn)深拷貝
deepCopy(item,b[attr])奔誓;
}else if(item instanceof Obejct){
b[attr] = {};
deepCopy(item,b[attr])
}else{
b[attr] = item;
}
}
}
deepCopy(a,aCopy);
修改a的引用數(shù)據(jù)favorite,就可以發(fā)現(xiàn)a搔涝,aCopy的不會(huì)一起改變
淺拷貝
object.assign 由于只復(fù)制了對(duì)象的值厨喂,是屬于淺拷貝
三點(diǎn)運(yùn)算符。等價(jià) Object.assign()庄呈,用這個(gè)替換復(fù)制數(shù)組最好蜕煌,原因看了上面的內(nèi)容就知道。
普通賦值拷貝
深拷貝
JSON.parse(JSON.stringify(arr/obj)): 數(shù)組或?qū)ο笊羁截? 但不能處理函數(shù)數(shù)據(jù)
遞歸遍歷