最開始意識到深拷貝的重要性是在我使用redux的時候(react + redux), redux的機制要求在reducer中必須返回乙個新的物件,而不能對原來的物件做改動,事實上,當時我當然不會主動犯這個錯誤,但很多時候,一不小心可能就會修改了原來的物件,例如:var newobj = obj; newobj.*** = *** 實際上,這個時候newobj和obj兩個引用指向的是同乙個物件,我修改了newobj,實際上也就等同於修改了obj,這,就是我和深淺拷貝的第一次相遇。。
**深拷貝和淺拷貝
深拷貝和淺拷貝的區別
1.淺拷貝: 將原物件或原陣列的引用直接賦給新物件,新陣列,新物件/陣列只是原物件的乙個引用
2.深拷貝: 建立乙個新的物件和陣列,將原物件的各項屬性的「值」(陣列的所有元素)拷貝過來,是「值」而不是「引用」
為什麼要使用深拷貝?
我們希望在改變新的陣列(物件)的時候,不改變原陣列(物件)
深拷貝的要求程度
我們在使用深拷貝的時候,一定要弄清楚我們對深拷貝的要求程度:是僅「深」拷貝第一層級的物件屬性或陣列元素,還是遞迴拷貝所有層級的物件屬性和陣列元素?
怎麼檢驗深拷貝成功
改變任意乙個新物件/陣列中的屬性/元素, 都不改變原物件/陣列
一、只對第一層級做拷貝
深拷貝陣列(只拷貝第一級陣列元素)
1.直接遍歷
var array =[1
,2,3
,4];
function
copy
(array)
return newarray;
}var copyarray =
copy
(array)
;copyarray[0]
=100
;console.
log(array)
;// [1, 2, 3, 4]
console.
log(copyarray)
;// [100, 2, 3, 4]
該方法就不做解釋(有腳就行)
slice()
var array =[1
,2,3
,4];
var copyarray = array.
slice()
;copyarray[0]
=100
;console.
log(array)
;// [1, 2, 3, 4]
console.
log(copyarray)
;// [100, 2, 3, 4]
slice() 方法返回乙個從已有的陣列中擷取一部分元素片段組成的新陣列(不改變原來的陣列!)
用法:array.slice(start,end) start表示是起始元素的下標, end表示的是終止元素的下標
當slice()不帶任何引數的時候,預設返回乙個長度和原陣列相同的新陣列
concat()
var array =[1
,2,3
,4];
var copyarray = array.
concat()
;copyarray[0]
=100
;console.
log(array)
;// [1, 2, 3, 4]
console.
log(copyarray)
;// [100, 2, 3, 4]
concat() 方法用於連線兩個或多個陣列。( 該方法不會改變現有的陣列,而僅僅會返回被連線陣列的乙個副本。)
用法:array.concat(array1,array2,…,arrayn)
因為我們上面呼叫concat的時候沒有帶上引數,所以var copyarray = array.concat();實際上相當於var copyarray = array.concat();
也即把返回陣列和乙個空陣列合併後返回
但是,事情當然不會這麼簡單,我上面的標題是 「深拷貝陣列(只拷貝第一級陣列元素)」,這裡說的意思是對於一級陣列元素是基本型別變數(如number,string,boolean)的簡單陣列, 上面這三種拷貝方式都能成功,但對第一級陣列元素是物件或者陣列等引用型別變數的陣列,上面的三種方式都將失效,例如:
var array =[,
,];var copyarray = array.
slice()
;copyarray[0]
.number =
100;
console.
log(array)
;// [, , ]
console.
log(copyarray)
;// [, , ]
深拷貝物件
1.直接遍歷
該方法不做解釋(有手就行)
2.es6的object.assign
var obj =
var copyobj = object.
assign
(, obj)
;copyobj.name =
'櫻花~~散!'
;console.
log(obj)
;//
console.
log(copyobj)
;//
object.assign:用於物件的合併,將源物件(source)的所有可列舉屬性,複製到目標物件(target),並返回合併後的target
用法: object.assign(target, source1, source2); 所以 copyobj = object.assign({}, obj); 這段**將會把obj中的一級屬性都拷貝到 {}中,然後將其返回賦給copyobj
3.es6擴充套件運算子:
擴充套件運算子(…)用於取出引數物件的所有可遍歷屬性,拷貝到當前物件之中
但是對多層巢狀物件,很遺憾,上面三種方法,都會失敗:
var obj =
, job:
'學生'
}var copyobj = object.
assign
(, obj)
copyobj.name.lastname =
'雷電女王的鬼鎧'
;console.
log(obj.name.lastname)
;// 雷電女王的鬼鎧
console.
log(copyobj.name.lastname)
;// 雷電女王的鬼鎧
二、拷貝所有層級
有沒有更強大一些的解決方案呢?使得我們能夠
1.不僅拷貝第一層級,還能夠拷貝陣列或物件所有層級的各項值
2. 不是單獨針對陣列或物件,而是能夠通用於陣列,物件和其他複雜的json形式的物件
請看下面:
下面這一招可謂是「一招鮮,吃遍天」
1.json.parse(json.stringify(***x))
var array =[,
,];var copyarray =
json
.parse
(json
.stringify
(array)
)copyarray[0]
.number =
100;
console.
log(array)
;// [, , ]
console.
log(copyarray)
;// [, , ]
簡單粗暴!!!
好用不?
嗯?你說啥? 你說上面的那種方法太無腦, 一定要自己寫一段遞迴才有做技術的感覺? ok成全你!
2.手動寫遞迴
var array =[,
,];function
copy
(obj);if
(typeof obj !==
'object'
)for
(var i in obj)
return newobj
}var copyarray =
copy
(array)
copyarray[0]
.number =
100;
console.
log(array)
;// [, , ]
console.
log(copyarray)
;// [, , ]
【注意】:上文的所有的示例都忽略了一些特殊的情況: 對物件/陣列中的function,正規表示式等特殊型別的拷貝
三、存在大量深拷貝需求的**——immutable提供的解決方案
實際上,即使我們知道了如何在各種情況下進行深拷貝,我們也仍然面臨一些問題: 深拷貝實際上是很消耗效能的。(我們可能只是希望改變新陣列裡的其中乙個元素的時候不影響原陣列,但卻被迫要把整個原陣列都拷貝一遍,這不是一種浪費嗎?)所以,當你的專案裡有大量深拷貝需求的時候,效能就可能形成了乙個制約的瓶頸了。
immutable的作用:
通過immutable引入的一套api,實現:
1.在改變新的陣列(物件)的時候,不改變原陣列(物件)
2.在大量深拷貝操作中顯著地減少效能消耗
先睹為快:
const
=require
('immutable'
)const map1 =
map(
)const map2 = map1.
set(
'b',50)
map1.
get(
'b')
// 2
map2.
get(
'b')
// 50
深拷貝和淺拷貝
淺拷貝就是物件的資料成員之間的簡單賦值,如你設計了乙個沒有類而沒有提供它的複製建構函式,當用該類的乙個物件去給令乙個物件賦值時所執行的過程就是淺拷貝,如 class a a private int data int main 這一句b a 就是淺拷貝,執行完這句後b.data 5 如果物件中沒有其他...
淺拷貝和深拷貝
以下情況都會呼叫拷貝建構函式 乙個物件以值傳遞的方式傳入函式體 例如 已知class a,class b void func a a void func a a func b b 此時函式對b的操作是呼叫拷貝建構函式後的臨時拷貝物件。多數傳指標 乙個物件以值傳遞的方式從函式返回 如 return b...
深拷貝和淺拷貝
ios提供了copy和mutablecopy方法,顧名思義,copy就是複製了乙個imutable的物件,而mutablecopy就是複製了乙個mutable的物件。以下將舉幾個例子來說明。1 系統的非容器類物件 這裡指的是nsstring nsnumber等等一類的物件。nsstring stri...