在使用JavaScript對(duì)數(shù)組進(jìn)行操作的時(shí)候,我們經(jīng)常需要將數(shù)組進(jìn)行備份吃引,事實(shí)證明如果只是簡(jiǎn)單的將它賦予其他變量,那么我們只要更改其中的任何一個(gè)珠移,然后其它的也會(huì)跟著改變的畴,這就導(dǎo)致了問題的發(fā)生。原因是內(nèi)存中僅保留一份數(shù)據(jù)足淆。這時(shí)候需要制作一份數(shù)據(jù)的副本巢块。只有復(fù)雜類型變量(引用類型)存在深拷貝與淺拷貝的問題,而基本類型沒有深拷貝的概念缸浦。
“堆內(nèi)存”和“棧內(nèi)存”
首先JavaScript中的變量分為基本類型和引用類型夕冲。基本類型就是保存在棧內(nèi)存中的簡(jiǎn)單數(shù)據(jù)段裂逐,而引用類型指的是那些保存在堆內(nèi)存中的對(duì)象歹鱼。
1、基本類型
基本類型有Undefined卜高、Null弥姻、Boolean、Number 和String掺涛。這些類型在內(nèi)存中分別占有固定大小的空間庭敦,他們的值保存在棧空間薪缆,我們通過按值來訪問的秧廉。
2、引用類型
引用類型拣帽,值大小不固定疼电,棧內(nèi)存中存放地址指向堆內(nèi)存中的對(duì)象。是按引用訪問的减拭。棧內(nèi)存中存放的只是該對(duì)象的訪問地址蔽豺,在堆內(nèi)存中為這個(gè)值分配空間。由于這種值的大小不固定拧粪,因此不能把它們保存到棧內(nèi)存中修陡。但內(nèi)存地址大小的固定的,因此可以將內(nèi)存地址保存在棧內(nèi)存中可霎。這樣魄鸦,當(dāng)查詢引用類型的變量時(shí),先從棧中讀取內(nèi)存地址癣朗, 然后再通過地址找到堆中的值号杏。對(duì)于這種,我們把它叫做按引用訪問。
當(dāng)我們看到一個(gè)變量類型是已知的盾致,就分配在棧里面主经,比如INT,Double等。其他未知的類型庭惜,比如自定義的類型罩驻,因?yàn)橄到y(tǒng)不知道需要多大,所以程序自己申請(qǐng)护赊,這樣就分配在堆里面惠遏。
JS數(shù)組的淺拷貝
簡(jiǎn)單的賦值就是淺拷貝。因?yàn)閷?duì)象和數(shù)組在賦值的時(shí)候都是引用傳遞骏啰。賦值的時(shí)候只是傳遞一個(gè)指針节吮。
var a = [1,2,3];
var b =a ;
var test = {name:'xiaohong', age:15};
var c = test;
console.log(a,b);
console.log(test);
console.log('-------------------');
b[0] =5;
c.age = 16;
console.log(a,b);
console.log(test);
console.log(c);
淺拷貝很容易,但是很多時(shí)候我們需要原樣的把數(shù)組或者對(duì)象復(fù)制一份判耕,在修改值的時(shí)候透绩,不改變初始對(duì)象的值。這個(gè)時(shí)候就需要使用深拷貝壁熄。
JS數(shù)組的深拷貝
方法一:js的slice函數(shù)
slice() 方法可從已有的數(shù)組中返回選定的元素帚豪。
【語(yǔ)法】arrayObject.slice(start,end)
【參數(shù)】arrayObj--必選項(xiàng):一個(gè)Array對(duì)象。start--必選項(xiàng):arrayObj中所指定的部分的開始元素是從零開始計(jì)算的下標(biāo)草丧。end--可選項(xiàng):arrayObj中所指定的部分的結(jié)束元素是從零開始計(jì)算的下標(biāo)狸臣。
【說明】
slice 方法返回一個(gè)Array對(duì)象,其中包含了arrayObj的指定部分昌执。slice方法一直復(fù)制到end所指定的元素烛亦,但是不包括該元素。如果start為負(fù)懂拾,將它作為length + start處理煤禽,此處length為數(shù)組的長(zhǎng)度。如果end為負(fù)委粉,就將它作為length + end處理呜师,此處length為數(shù)組的長(zhǎng)度娶桦。如果省略end 贾节,那么slice方法將一直復(fù)制到 arrayObj 的結(jié)尾。如果end出現(xiàn)在start之前衷畦,不復(fù)制任何元素到新數(shù)組中栗涂。
實(shí)例:
var a = [1,2,3,4,5];
var b = a.slice(0,2);
var c = a.slice(-3,-1);
var d = a.slice(-1,-3);
console.log(b,c,d);
方法二:js的concat函數(shù)
concat() 方法用于連接兩個(gè)或多個(gè)數(shù)組。該方法不會(huì)改變現(xiàn)有的數(shù)組祈争,而僅僅會(huì)返回被連接數(shù)組的一個(gè)副本斤程。
【語(yǔ)法】arrayObject.concat(arrayX,arrayX,......,arrayX)
【參數(shù)】arrayX--必需:該參數(shù)可以是具體的值,也可以是數(shù)組對(duì)象》奘可以是任意多個(gè)扁藕。
【說明】
返回一個(gè)新的數(shù)組。該數(shù)組是通過把所有 arrayX 參數(shù)添加到 arrayObject 中生成的疚脐。如果要進(jìn)行 concat()操作的參數(shù)是數(shù)組亿柑,那么添加的是數(shù)組中的元素,而不是數(shù)組棍弄。
實(shí)例:
var a = [1,2,3];
var b = a.concat(4,5);
console.log(b);
var c = [6,7];
var d = a.concat(c);
console.log(d);
除了上述兩個(gè)方法外望薄,還有沒有其他的深拷貝方法?
js遍歷數(shù)組的方法:
var a = [1,2,3];
var b = [];
function deepCopy(arry1,arry2) {
for(i=0;i < arry1.length;i++) {
arry2[i] = arry1[i];
}
}
deepCopy(a,b);
a[0] = "A";
console.log(a,b);
slice()呼畸、concat()的局限性在哪里痕支?
var a = [1,2,{"name":"張三"},{"name":"李四"},[4,5]];
var b = a.slice(0);
var c = [].concat(a);
a[2].name="王五";
a[4][0] = "A";
console.log(a);
console.log(b);
console.log(c);
由上面的例子可以看出,由于數(shù)組內(nèi)部存在對(duì)象和數(shù)組蛮原,當(dāng)改變對(duì)象屬性和內(nèi)部數(shù)組的元素后卧须,深拷貝的b和c同樣也發(fā)生了改變。因此瞬痘,slice和concat這兩個(gè)方法故慈,僅適用于對(duì)不包含引用對(duì)象的一維數(shù)組的深拷貝。