js物件的深拷貝及其的幾種方法
深拷貝和淺拷貝是javascript中一個比較複雜的問題,也是面試官最喜歡問的問題之一,通過這個為可以看出是否入門,深拷貝和淺拷貝也是初學者經常犯錯一個點。
簡單來說深拷貝是拷貝儲存在棧中的物件,而淺拷貝是從記憶體中拷貝,這就涉及到資料存放位置了,總所周知,資料大體可以分為兩種資料型別,一種是基本資料型別,資料結構不是很複雜,單獨可以存在記憶體中就可以,而另一種是複雜資料型別,也叫引用資料型別,例如陣列和物件,是放在棧中儲存的,而基本資料型別是放在記憶體中的,不涉及深拷貝和淺拷貝,也可以說基本資料型別都是深拷貝
而引用型別資料儲存比較複雜,例如var a=[1,3,4] 這句話的儲存就是首先在記憶體開闢一個空間,但是記憶體當中不可以儲存這種複雜資料型別,所以要把這種結構放到棧當中,棧相當於一個密碼箱,而鑰匙存在記憶體當中,這就構成了一個儲存關係,淺拷貝var b=a簡單來說就是把這份鑰匙複製了一份,但記憶體當中的資料並沒有複製,所以如果改變a相應b也會改變
而深拷貝則需要在棧中在生成一個密碼箱,生成一把新鑰匙(鑰匙2),這樣深拷貝的兩種數值不會相互影響,也可以說沒有任何關係了
方法:
1:jq使用,$.extend({},obj)
2:Object.assign({},obj)
這兩種比較基礎,估計都會使用
3:clone(obj)
var clone = function (obj) { return JSON.parse(JSON.stringify(obj)); }
這種方法有種缺陷,這種方法會忽略值為function以及undefied的欄位,而且對date型別的支援也不太友好。
4:clone(obj)
var clone = function (obj) { if(obj === null) return null if(typeof obj !== 'object') return obj; if(obj.constructor===Date) return new Date(obj); var newObj = new obj.constructor (); //保持繼承鏈 for (var key in obj) { if (obj.hasOwnProperty(key)) { //不遍歷其原型鏈上的屬性 var val = obj[key]; newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除與函式名的耦合 } } return newObj; };
這種方法也就使封裝最好的深拷貝的方法,以下為解釋:
1、用new obj.constructor ()
建構函式新建一個空的物件,而不是使用{}
或者[]
,這樣可以保持原形鏈的繼承;
2、用obj.hasOwnProperty(key)
來判斷屬性是否來自原型鏈上,因為for..in..
也會遍歷其原型鏈上的可列舉屬性。
3、上面的函式用到遞迴演算法,在函式有名字,而且名字以後也不會變的情況下,這樣定義沒有問題。但問題是這個函式的執行與函式名 factorial 緊緊耦合在了一起。為了消除這種緊密耦合的現象,需要使用 arguments.callee
。