因為近日的項目需要用到對數(shù)組做深克隆,所以做一個簡要介紹與總結。
一開始聽說這個概念的時候嚇一跳,覺得高深莫測,有點怕怕啊。完了了解一下,哦,原來就這玩意啊~~~
那么什么是深克隆,什么是淺克隆呢?
首先,克隆只針對對象、數(shù)組、函數(shù)等復雜數(shù)據(jù)。
淺克隆就是將棧內存中的引用復制一份,賦給一個新的變量,本質上兩個指向堆內存中的同一地址,內容也相同,其中一個變化另一個內容也會變化(根本上改變的是同一個對象)。深克隆就是創(chuàng)建一個新的空對象,開辟一塊內存,然后將原對象中的數(shù)據(jù)全部復制過去,完全切斷兩個對象間的聯(lián)系。
還是show code to 表達:
var a = new Array;
a[0] = "小明";
a[1] = "小紅";
a[2] = "小光";
b = a;
alert(b);
這里簡單定義了一個數(shù)組a,然后加入三個元素,再把a賦給變量b,輸入b后發(fā)現(xiàn)內容是a的內容。這里涉及到一些內存位置的內容,簡單說說。這里創(chuàng)建出的數(shù)組(姑且叫他array)被放在堆內存中,而a,b兩個引用都是在棧內存中。array包含了三個元素,a和b則僅僅是一個地址,指向了array,和c語言中的指針非常相似。這里就是一個簡單的淺克隆的例子,把數(shù)組array的引用復制一份給b,讓兩個變量都指向array。這時候任意一個引用對array做出的修改都會折射到另一個引用上。
明白了淺克隆,這里也解釋一下為什么我開始說深淺克隆只針對對象數(shù)組函數(shù)等。先說說棧,棧內存有很多優(yōu)點,比如讀取速度很快,僅次于寄存器;棧內存數(shù)據(jù)可以共享。但也有缺點,棧內存大小和生存期必須是確定的,缺乏靈活性。而因為基本變量(js的原始值undefined,null,number,string,boolean 類型)一般所占內存一半較小且大小固定,所以一般放在棧中,如果放在堆中可能查找耗時較多。堆內存中的數(shù)組、函數(shù)、對象等,一方面可能較大,另一方面不確定大小,所以不能放在棧中,只能在堆里面,由棧內存中的句柄(你也可以叫他指針、引用happy jiu ok)加以引用操作。深淺克隆只能針對在堆內存中的,有句柄引用的復雜對象。簡單的直接查就OK啦,沒必要那么那么復雜。。。
差點跑題,趕緊拉回來。內存方面如果想要深入了解可以自行Google,在此不再贅述。
深克隆本質上是創(chuàng)造一個完全一樣的對象,這里簡單介紹兩種js的deepClone方法。talk is cheap show me the code~
一、遞歸完成深克隆
不多說直接上代碼
function deepClone(arr) {
if (typeof arr != "object") {
return arr;
}
var result = {};
for (var i in arr) {
result[i] = deepClone(arr[i]);
}
return result;
}
代碼非常簡單,只有十行代碼。首先判斷元素是否是object,不是打哪兒來回哪兒去,然后創(chuàng)建一個對象賦給result。然后遍歷arr中所有的元素,遞歸判斷是否是深層對象,不是返回賦給result。這樣就實現(xiàn)了深克隆。下面測試一下。
var a = {
name:"小明",
age:"12",
sister:{
name:"小美",
age:"10"
}
}
var arrTwo = arrOne;
var arrThree = deepClone(a);
arrThree.name = "小光";
arrThree.object.name = "小雅";
console.log(arrTwo);
console.log(arrThree);
輸出結果也在意料之中
Object:
name:"小明",
age:"12",
Object:
name:"小美",
age:"10"
Object:
name:"小光",
age:"12",
Object:
name:"小雅",
age:"10"
這里可以看到在對原數(shù)組操作時,克隆返回的數(shù)組內容不會改變。兩個數(shù)組是完全不同的兩個對象,在堆內存中各占一塊,沒有關聯(lián)。
二、利用JSON
利用json簡簡單單輕輕松松就能搞定,多簡單呢?還是用上面的例子
var a = {
name:"小明",
age:"12",
sister:{
name:"小美",
age:"10"
}
}
var arrTwo = arrOne;
var arrThree = deepClone(a); //12行
arrThree.name = "小光";
arrThree.object.name = "小雅";
console.log(arrTwo);
console.log(arrThree);
把12行稍微改動一下
var a = {
name:"小明",
age:"12",
sister:{
name:"小美",
age:"10"
}
}
var arrTwo = arrOne;
var arrThree = JSON.parse(JSON.stringify(arrTwo)); //12行
arrThree.name = "小光";
arrThree.object.name = "小雅";
console.log(arrTwo);
console.log(arrThree);
最簡單的深克隆,一行代碼搞定,拿好不謝!
臥槽臥槽,內心萬馬奔騰有木有?我又是遞歸又是各種判斷for創(chuàng)建對象的,你一行就搞定了?但是事實上真的一行代碼就OK了~~~(那你上面扯那么多亂七八糟的QAQ)。
好的平復一下心理落差,來了解一下這個方法怎么實現(xiàn)的啊。為了防止長篇大論,我就不具體說json了,自行Google一下。直接說說parse和stringify方法。
JSON.parse() :
Parse a string as JSON, optionally transform the produced value and its properties, and return the value.
JSON.stringify() :
Return a JSON string corresponding to the specified value, optionally including only certain properties or replacing property values in a user-defined manner.
這里是摘自dash文檔的對兩個方法的介紹,parse方法用于將字符串解析為 JSON,可以任意轉換生成的值及其屬性,并返回值。sretingify方法用于從一個對象中解析出json字符串。
什么意思呢?就是將一個對象先解析為json對象,然后再解析成object對象。嗯,so easy~~變來變去順道創(chuàng)建個對象完成復制。
總結
如一開始說的,原來真的就是這玩意~~~所謂深克隆就是創(chuàng)造一個完全一樣的對象,將原對象的的所有元素拷貝過來即可。
淺克隆就是復制一份引用,所有引用指向同一份數(shù)據(jù)。
嗯,確實蠻簡單的嘛哈哈哈??