前端筆試或者面試的時候,很喜歡問的一個問題就是物件的深度克隆,或者說是物件的深度複製。其實這個問題說容易很容易,但是要說全面也挺不易。
要弄明白物件的克隆,首先要明白js中物件的組成。在js中一切實體皆是物件,具體分為原始型別和合成型別。原始型別物件指的是number、string、boolean等,合成型別物件指的是array、object以及function。
又或許你剛聽說“深度克隆”這個詞,簡單來說,就是說有個變數a,a的值是個物件(包括基本資料型別),現在你要建立一個變數b,使得它擁有跟a一樣的方法和屬性等等。但是a和b之間不能相互影響,即a的值的改變不影響b值的變化。
直接賦值可好?
var a = 1;
var b = a;
a = 10;
console.log(b);
// 1
var a = ‘hello’;
var b = a;
a = ‘world’;
console.log(b);
// hello
var a = true;
var b = a;
a = false;
console.log(b);
// true
實踐證明某些JavaScript的原始資料型別,如果要克隆直接賦值即可。
關於function的深度複製:查閱了一些資料,function的深度複製似乎和原始資料型別的深度複製一樣。
var a = function() {console.log(1);};
var b = a;
a = function() {console.log(2);};
b();
// 1
本來我也是這麼認為的,直到文章下出現了評論。思考後我覺得function和普通的物件一樣,只是我們在平常應用中習慣了整體的重新賦值,導致它在深度複製中的表現和原始型別一致:
var a = function() {console.log(1);};
a.tmp = 10;
var b = a;
a.tmp = 20;
console.log(b.tmp);
// 20
於是乎對於function型別的深度克隆,直接賦值似乎並不應該是一種最好的方法(儘管實際應用中足矣)。那麼如何克隆?可以參考文章下麵的評論,不失為一種解決方案。
但是物件呢?
var a = [0, 1, 2, 3];
var b = a;
a.push(4);
console.log(b);
// [0, 1, 2, 3, 4]
顯然與預期不符,為什麼會這樣?因為原始資料型別儲存的是物件的實際資料,而物件型別儲存的是物件的取用地址。上面的例子呢也就是說a和b物件取用了同一個地址,無論改變a還是改變b,其實根本操作是一樣的,都是對那塊空間地址中的值的改變。
於是我們知道了,對於基本的物件來說,不能只能用=賦值,思索後寫下如下程式碼:
function deepClone(obj) {
var o = obj instanceof Array ? [] : {};
for(var k in obj)
o[k] = typeof obj[k] === Object ? deepClone(obj[k]) : obj[k];
return o;
}
var a = [[1, 2, 3], [4, 5, 6, 7]];
var b = deepClone(a);
console.log(b);
似乎可以解決一般的物件(包括Array)的深度克隆了,或許這兒會有疑問,new String(..)這類的也是物件啊,可是這樣寫你克隆不了啊…但是樓主覺得深度克隆的考點不在這裡,可能在於:
-
原始資料型別的直接賦值
-
function的exception
-
物件的深度克隆中Array型別的判斷
-
克隆函式的遞迴呼叫
來自:韓子遲
連結:http://www.cnblogs.com/zichi/p/4568150.html