查閱資料,看到有關深淺拷貝上面的誤區,有人說數組的slice()與concat()方法實現的是深拷貝。對此我作了一些嘗試並詳細理了一下關於js的深淺拷貝問題。首先咱們要知道數據類型的存儲方式——javascript
js中變量分爲兩類:java
基本類型:undefined,null,字符串,數值,布爾json
引用類型:統稱爲object。具體的有Object,Array,Function等數組
重點是這兩種類型的存儲方式了:基本類型的數據是存放在棧內存中的,而引用類型的數據是存放在堆內存中的。spa
基本數據類型,是這個樣子的:3d
引用類型保存在堆中,棧內的是變量的標識符以及對象在堆內存中的存儲地址,當須要訪問引用類型(如對象,數組等)的值時,首先從棧中得到該對象的地址指針,而後再從對應的堆內存中取得所需的數據。指針
因此對於這兩種類型的賦值是有不一樣的:對象
當你在複製基本類型的時候,至關於把值也一併複製給了新的變量。修改值的時候也不會影響另外一個變量的值。blog
而在複製引用類型的時候,實際上只是複製了指向堆內存的地址,即原來的變量與複製的新變量指向了同一個東西。繼承
例1.改變a,會影響b
var a = {name:"peri",age:20}; var b = a; console.log(a === b); a.age = 30; console.log(a); console.log(b);
對於僅僅是複製了引用(地址),即原來的變量和新的變量指向同一個地址,彼此之間的操做會互相影響,爲淺拷貝。
而若是是在堆中從新分配內存,擁有不一樣的地址,複製後的對象與原來的對象徹底隔離,互不影響,爲深拷貝。
深淺拷貝的主要區別就是:複製的是引用(地址)仍是複製的是實例。
對於例1,咱們能夠經過遞歸的方式來實現深拷貝,對引用類型進行遍歷,一直到是基本類型爲止。
function deepClone(source){ if(!source && typeof source !== 'object'){ throw new Error('error arguments', 'shallowClone'); } var targetObj = Array.isArray(source) ? [] : {}; for(var keys in source){ //只遍歷對象自身的屬性,而不包含繼承於原型鏈上的屬性。 if(source.hasOwnProperty(keys)){ if(source[keys] && typeof source[keys] === 'object'){ targetObj[keys] = deepClone(source[keys]); //遞歸 }else{ targetObj[keys] = source[keys]; } } } return targetObj; } var a = {name:"peri",age:20}; var b = deepClone(a); console.log(a === b); a.age = 18; console.log(a); console.log(b);
stringify把一個 js 對象序列化爲一個 JSON 字符串。parse把 JSON 字符串反序列化爲一個 js 對象,這兩個方法實現的是深拷貝。
var obj = {name:'pp',age:24,company : { name : 'AAA', address : '深圳'} }; var obj_json = JSON.parse(JSON.stringify(obj)); console.log(obj === obj_json); obj.company.name = "BBB"; obj.name = "peri"; console.log(obj); console.log(obj_json);
jQuery的$.extend方法是咱們在開發中常常用到的方法,用於合併若干個對象。
可用於深淺拷貝,第一個參數爲true深拷貝。爲false或者沒有爲淺拷貝。
例2.深拷貝
var obj = {name:'pp',age:24,company : { name : 'AAA', address : '深圳'} }; var obj_json = $.extend(true, {}, obj); console.log(obj === obj_json); obj.company.name = "BBB"; obj.name = "peri"; console.log(obj); console.log(obj_json);
例2.2淺拷貝
最後再來討論一下最初關於數組的slice()與concat()方法是否深拷貝。
例3.slice()方法
var a = [1,2,3]; var b = a.slice(); console.log(b === a); a[0] = 4; console.log(a); console.log(b);
能夠看到,改變a並無影響b!那麼就說明slice()是深拷貝了嗎?沒有那麼簡單!
例3.1
var a = [[1,2,3],4,5]; var b = a.slice(); console.log(a === b); a[0][0] = 6; console.log(a); console.log(b);
這很明顯說明slice()不是深拷貝!
原來Array 的 slice() 和 concat ()方法,對於第一層的值都是深拷貝,而到第二層的時候 Array的slice() 和 concat()方法就是複製引用 。(concat與slice結果相同,這裏再也不舉例)。