?一蛔溃、淺拷貝
在定義一個(gè)對(duì)象或數(shù)組時(shí),變量存放的往往是一個(gè)引用地址瓦宜。當(dāng)我們使用對(duì)象拷貝時(shí)蔚万,如果屬性是對(duì)象或數(shù)組時(shí)這是我們傳遞的也只是一個(gè)地址。因此子對(duì)象在訪問(wèn)該屬性時(shí)临庇,會(huì)根據(jù)地址回溯到父對(duì)象指向的堆內(nèi)存中反璃,即父子對(duì)象發(fā)生了關(guān)聯(lián),兩者的屬性值會(huì)指向同一內(nèi)存空間假夺。
var a = {key1: '1'};
function copy(p) {
? ? var c = {};
? ? for(var i in p){
? ? ? ? c[i] = p[i]
????}
? ? return c;
}
a.key2 = ['1', '2', '3'];
var b = copy(a);
b.key3 = '3';
b.key2.push('4');
console.log(a);? ? // {key1: '1', key2: ['1', '2', '3', '4']}
console.log(b);? ? // {key1: '1', key2:?['1', '2', '3', '4'], key3: '3'}
但是若是修改的屬性變?yōu)閷?duì)象或數(shù)組時(shí)淮蜈,那么父子對(duì)象之間就發(fā)生關(guān)聯(lián),從上可知:
原因是key1的值屬于基本類型已卷,所以拷貝的時(shí)候傳遞的就是該數(shù)據(jù)段梧田;但是key2的值是堆內(nèi)存中的對(duì)象,所以key2在拷貝的時(shí)候傳遞的是指向key2對(duì)象的地址侧蘸,無(wú)論復(fù)制多少個(gè)key2裁眯,其值始終是指向父對(duì)象的key2對(duì)象的內(nèi)存空間。
實(shí)現(xiàn)淺拷貝的方法:
var a = {name: '123'};
var b = Object.assign({}, a);
b.age = 1;
console.log(a.age);? ? // undefined
var a = [1,2,3];
var b = a.slice();
b.push(4);
console.log(a);? ? // [1,2,3]
console.log(b);? ? // [1,2,3,4]
var a = [1,2,3];
var b = a.concat();
b.push(4);
console.log(a);? ? // [1,2,3]
console.log(b);? ? // [1,2,3,4]
var a = [1,2,3];
var b = [...a];
console.log(a);? ? // [1,2,3]
console.log(b);? ? // [1,2,3,4]
二闺魏、深拷貝
獲取以上并不是我們?cè)趯?shí)際編碼中想要的結(jié)果未状,我們不希望父子對(duì)象之間的關(guān)聯(lián),那么這時(shí)候就可以用到深拷貝析桥。既然屬性值類型是數(shù)組或?qū)ο髸r(shí)只會(huì)傳地址司草,那么我們就用遞歸來(lái)解決這個(gè)問(wèn)題。把父對(duì)象中所有屬于對(duì)象的屬性類型都遍歷賦給子對(duì)象即可泡仗。
var a = [key: '1'];
function copy(p, c){
? ? var c = c || {};
? ? for(var i in p){
? ? ? ? if(typeof p[i] === 'object') {
? ? ? ? ? ? c[i] = (p[i].constructor === Array)?[]:{}
? ? ? ? ? ? copy(p[i], c[i])
????????} else {
? ? ? ? ? ? c[i] = p[i]
????????}
????}
? ? return c;
}
a.key2 = [1,2,3];
var b = {};
b = copy(a, b);
b.key2.push(4);
console.log(a.key2);? ? // [1,2,3]
console.log(b.key2);? ? // [1,2,3,4]
最后:?總結(jié)基本數(shù)據(jù)類型和引用數(shù)據(jù)類型區(qū)別
1埋虹、聲明變量時(shí)內(nèi)存分配不同
*原始類型:在棧中,因?yàn)檎紦?jù)空間是固定的娩怎,可以將他們存在較小的內(nèi)存中-棧中搔课,這樣便于迅速查詢變量的值
*引用類型:存在堆中,棧中存儲(chǔ)的變量截亦,只是用來(lái)查找堆中的引用地址爬泥。
? ?這是因?yàn)椋阂弥档拇笮?huì)改變,所以不能把它放在棧中崩瓤,否則會(huì)降低變量查尋的速度袍啡。相反,放在變量的椚赐埃空間中的值是該對(duì)象存儲(chǔ)在堆中的地址境输。地址的大小是固定的蔗牡,所以把它存儲(chǔ)在棧中對(duì)變量性能無(wú)任何負(fù)面影響
2、不同的內(nèi)存分配帶來(lái)不同的訪問(wèn)機(jī)制
在javascript中是不允許直接訪問(wèn)保存在堆內(nèi)存中的對(duì)象的嗅剖,所以在訪問(wèn)一個(gè)對(duì)象時(shí)辩越,首先得到的是這個(gè)對(duì)象在堆內(nèi)存中的地址,然后再按照這個(gè)地址去獲得這個(gè)對(duì)象中的值信粮,這就是傳說(shuō)中的按引用訪問(wèn)黔攒。
? ? 而原始類型的值則是可以直接訪問(wèn)到的。
3蒋院、復(fù)制變量時(shí)的不同
1)原始值:在將一個(gè)保存著原始值的變量復(fù)制給另一個(gè)變量時(shí)亏钩,會(huì)將原始值的副本賦值給新變量莲绰,此后這兩個(gè)變量是完全獨(dú)立的欺旧,他們只是擁有相同的value而已。
2)引用值:在將一個(gè)保存著對(duì)象內(nèi)存地址的變量復(fù)制給另一個(gè)變量時(shí)蛤签,會(huì)把這個(gè)內(nèi)存地址賦值給新變量辞友,
也就是說(shuō)這兩個(gè)變量都指向了堆內(nèi)存中的同一個(gè)對(duì)象,他們中任何一個(gè)作出的改變都會(huì)反映在另一個(gè)身上震肮。
(這里要理解的一點(diǎn)就是称龙,復(fù)制對(duì)象時(shí)并不會(huì)在堆內(nèi)存中新生成一個(gè)一模一樣的對(duì)象,只是多了一個(gè)保存指向這個(gè)對(duì)象指針的變量罷了)戳晌。多了一個(gè)指針
4鲫尊、參數(shù)傳遞的不同(把實(shí)參復(fù)制給形參的過(guò)程)
首先我們應(yīng)該明確一點(diǎn):ECMAScript中所有函數(shù)的參數(shù)都是按值來(lái)傳遞的。
但是為什么涉及到原始類型與引用類型的值時(shí)仍然有區(qū)別呢沦偎?還不就是因?yàn)閮?nèi)存分配時(shí)的差別疫向。
1)原始值:只是把變量里的值傳遞給參數(shù),之后參數(shù)和這個(gè)變量互不影響豪嚎。
2)引用值:對(duì)象變量它里面的值是這個(gè)對(duì)象在堆內(nèi)存中的內(nèi)存地址搔驼,這一點(diǎn)你要時(shí)刻銘記在心!
因此它傳遞的值也就是這個(gè)內(nèi)存地址侈询,這也就是為什么函數(shù)內(nèi)部對(duì)這個(gè)參數(shù)的修改會(huì)體現(xiàn)在外部的原因了舌涨,因?yàn)樗鼈兌贾赶蛲粋€(gè)對(duì)象。