摘要:它將返回目標對象。有些文章說是深拷貝,其實這是不正確的。深拷貝相比于淺拷貝速度較慢并且花銷較大。拷貝前后兩個對象互不影響。使用深拷貝的場景完全改變變量之后對沒有任何影響,這就是深拷貝的魔力。
一、賦值(Copy)
賦值是將某一數值或對象賦給某個變量的過程,分為:
1、基本數據類型:賦值,賦值之后兩個變量互不影響
2、引用數據類型:賦址,兩個變量具有相同的引用,指向同一個對象,相互之間有影響
對基本類型進行賦值操作,兩個變量互不影響。
// saucxs let a = "saucxs"; let b = a; console.log(b); // saucxs a = "change"; console.log(a); // change console.log(b); // saucxs
對引用類型進行賦址操作,兩個變量指向同一個對象,改變變量 a 之后會影響變量 b,哪怕改變的只是對象 a 中的基本類型數據。
// saucxs let a = { name: "saucxs", book: { title: "You Don"t Know JS", price: "45" } } let b = a; console.log(b); // { // name: "saucxs", // book: {title: "You Don"t Know JS", price: "45"} // } a.name = "change"; a.book.price = "55"; console.log(a); // { // name: "change", // book: {title: "You Don"t Know JS", price: "55"} // } console.log(b); // { // name: "change", // book: {title: "You Don"t Know JS", price: "55"} // }
通常在開發中并不希望改變變量 a 之后會影響到變量 b,這時就需要用到淺拷貝和深拷貝。
二、淺拷貝(Shallow Copy) 1、什么是淺拷貝創建一個新對象,這個對象有著原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值,如果屬性是引用類型,拷貝的就是內存地址 ,所以如果其中一個對象改變了這個地址,就會影響到另一個對象。
上圖中,SourceObject 是原對象,其中包含基本類型屬性 field1 和引用類型屬性 refObj。淺拷貝之后基本類型數據 field2 和 filed1 是不同屬性,互不影響。但引用類型 refObj 仍然是同一個,改變之后會對另一個對象產生影響。
簡單來說可以理解為淺拷貝只解決了第一層的問題,拷貝第一層的基本類型值,以及第一層的引用類型地址。
2、淺拷貝使用場景 2.1 Object.assign()Object.assign() 方法用于將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。
有些文章說Object.assign() 是深拷貝,其實這是不正確的。
// saucxs let a = { name: "saucxs", book: { title: "You Don"t Know JS", price: "45" } } let b = Object.assign({}, a); console.log(b); // { // name: "saucxs", // book: {title: "You Don"t Know JS", price: "45"} // } a.name = "change"; a.book.price = "55"; console.log(a); // { // name: "change", // book: {title: "You Don"t Know JS", price: "55"} // } console.log(b); // { // name: "saucxs", // book: {title: "You Don"t Know JS", price: "55"} // }
上面代碼改變對象 a 之后,對象 b 的基本屬性保持不變。但是當改變對象 a 中的對象 book 時,對象 b 相應的位置也發生了變化。
2.2 展開語法 Spread// saucxs let a = { name: "saucxs", book: { title: "You Don"t Know JS", price: "45" } } let b = {...a}; console.log(b); // { // name: "saucxs", // book: {title: "You Don"t Know JS", price: "45"} // } a.name = "change"; a.book.price = "55"; console.log(a); // { // name: "change", // book: {title: "You Don"t Know JS", price: "55"} // } console.log(b); // { // name: "saucxs", // book: {title: "You Don"t Know JS", price: "55"} // }2.3 Array.prototype.slice方法
slice不會改變原數組,slice() 方法返回一個新的數組對象,這一對象是一個由 begin和 end(不包括end)決定的原數組的淺拷貝。
// saucxs let a = [0, "1", [2, 3]]; let b = a.slice(1); console.log(b); // ["1", [2, 3]] a[1] = "99"; a[2][0] = 4; console.log(a); // [0, "99", [4, 3]] console.log(b); // ["1", [4, 3]]
可以看出,改變 a[1] 之后 b[0] 的值并沒有發生變化,但改變 a[2][0] 之后,相應的 b[1][0] 的值也發生變化。
說明 slice() 方法是淺拷貝,相應的還有concat等,在工作中面對復雜數組結構要額外注意。
三、深拷貝(Deep Copy) 3.1 什么是深拷貝?深拷貝會拷貝所有的屬性,并拷貝屬性指向的動態分配的內存。當對象和它所引用的對象一起拷貝時即發生深拷貝。深拷貝相比于淺拷貝速度較慢并且花銷較大。拷貝前后兩個對象互不影響。
3.2 使用深拷貝的場景 3.2.1 JSON.parse(JSON.stringify(object))// saucxs let a = { name: "saucxs", book: { title: "You Don"t Know JS", price: "45" } } let b = JSON.parse(JSON.stringify(a)); console.log(b); // { // name: "saucxs", // book: {title: "You Don"t Know JS", price: "45"} // } a.name = "change"; a.book.price = "55"; console.log(a); // { // name: "change", // book: {title: "You Don"t Know JS", price: "55"} // } console.log(b); // { // name: "saucxs", // book: {title: "You Don"t Know JS", price: "45"} // }
完全改變變量 a 之后對 b 沒有任何影響,這就是深拷貝的魔力。
我們看下對數組深拷貝效果如何。
// saucxs let a = [0, "1", [2, 3]]; let b = JSON.parse(JSON.stringify( a.slice(1) )); console.log(b); // ["1", [2, 3]] a[1] = "99"; a[2][0] = 4; console.log(a); // [0, "99", [4, 3]] console.log(b); // ["1", [2, 3]]
對數組深拷貝之后,改變原數組不會影響到拷貝之后的數組。
但是該方法有以下幾個問題:
(1)會忽略 undefined
(2)會忽略 symbol
(3)不能序列化函數
(4)不能解決循環引用的對象
(5)不能正確處理new Date()
(6)不能處理正則
其中(1)(2)(3) undefined、symbol 和函數這三種情況,會直接忽略。
// saucxs let obj = { name: "saucxs", a: undefined, b: Symbol("saucxs"), c: function() {} } console.log(obj); // { // name: "saucxs", // a: undefined, // b: Symbol(saucxs), // c: ? () // } let b = JSON.parse(JSON.stringify(obj)); console.log(b); // {name: "saucxs"}
其中(4)循環引用會報錯
// saucxs let obj = { a: 1, b: { c: 2, d: 3 } } obj.a = obj.b; obj.b.c = obj.a; let b = JSON.parse(JSON.stringify(obj)); // Uncaught TypeError: Converting circular structure to JSON
其中(5)* new Date 情況下,轉換結果不正確。
// saucxs new Date(); // Mon Dec 24 2018 10:59:14 GMT+0800 (China Standard Time) JSON.stringify(new Date()); // ""2018-12-24T02:59:25.776Z"" JSON.parse(JSON.stringify(new Date())); // "2018-12-24T02:59:41.523Z"
解決方法轉成字符串或者時間戳就好了。
// saucxs let date = (new Date()).valueOf(); // 1545620645915 JSON.stringify(date); // "1545620673267" JSON.parse(JSON.stringify(date)); // 1545620658688
其中(6)正則情況下
// saucxs let obj = { name: "saucxs", a: /"123"/ } console.log(obj); // {name: "saucxs", a: /"123"/} let b = JSON.parse(JSON.stringify(obj)); console.log(b); // {name: "saucxs", a: {}}
PS:為什么會存在這些問題可以學習一下 JSON。
除了上面介紹的深拷貝方法,
常用的還有jQuery.extend() 和 lodash.cloneDeep(),后面文章會詳細介紹源碼實現。
四、總結和原數據是否指向同一對象 第一層數據為基本數據類型 原數據中包含子對象
賦值 是 改變會使原數據一起改變 改變會使原數據一起改變
淺拷貝 否 改變不會使原數據一起改變 改變會使原數據一起改變
深拷貝 否 改變不會使原數據一起改變 改變不會使原數據一起改變
1、深拷貝和淺拷貝
2、MDN之Object.assign()
3、MDN之展開語法
4、MDN之Array.stypetype.slice()
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/109567.html
摘要:展開語法木易楊通過代碼可以看出實際效果和是一樣的。木易楊可以看出,改變之后的值并沒有發生變化,但改變之后,相應的的值也發生變化。深拷貝使用場景木易楊完全改變變量之后對沒有任何影響,這就是深拷貝的魔力。木易楊情況下,轉換結果不正確。 一、賦值(Copy) 賦值是將某一數值或對象賦給某個變量的過程,分為下面 2 部分 基本數據類型:賦值,賦值之后兩個變量互不影響 引用數據類型:賦址,兩個...
摘要:引用類型值引用類型值是保存在堆內存中的對象,變量保存的只是指向該內存的地址,在復制引用類型值的時候,其實只復制了指向該內存的地址。 前言 要理解 JavaScript中淺拷貝和深拷貝的區別,首先要明白JavaScript的數據類型。JavaScript有兩種數據類型,基礎數據類型和引用數據類型。js的基本類型:undefined,null,string,boolean,number,s...
摘要:前言里面淺拷貝和深拷貝是非常關鍵的知識點,今天就來通過本文清楚的了解深淺拷貝以及該如何實現這兩種拷貝方式。對象的拷貝又分為淺拷貝和深拷貝。印證了上述所說的對于所有的基本類型,簡單的賦值已經是實現了深拷貝。 前言 JavaScript里面淺拷貝和深拷貝是非常關鍵的知識點,今天就來通過本文清楚的了解深淺拷貝以及該如何實現這兩種拷貝方式。 深淺拷貝的區別 拷貝:其實就是一個對象復制給另外...
摘要:要理解中淺拷貝和深拷貝的區別,首先要明白的數據類型有兩種數據類型,基礎數據類型和引用數據類型基礎數據類型保存在棧內存中的簡單數據段,有,,,,引用數據類型,對象,保存在堆內存空間中存放在棧內存中與變量名與內存地址存儲在棧內存中,與作為對象存 要理解 JavaScript中淺拷貝和深拷貝的區別,首先要明白JavaScript的數據類型 JavaScript有兩種數據類型,基礎數據類型和引...
摘要:但是進行的是淺拷貝,拷貝的是屬性值。對象展開符深拷貝的實現方式手動復制轉成再轉回來只有可以轉成格式的對象才可以這樣用,像沒辦法轉成沒被改到使用方法避免相互引用對象導致死循環,如的情況四參考關于的淺拷貝和深拷貝 一、理解 淺拷貝只復制指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存。但深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。...
閱讀 1457·2023-04-25 19:00
閱讀 4158·2021-11-17 17:00
閱讀 1771·2021-11-11 16:55
閱讀 1530·2021-10-14 09:43
閱讀 3132·2021-09-30 09:58
閱讀 860·2021-09-02 15:11
閱讀 2130·2019-08-30 12:56
閱讀 1408·2019-08-30 11:12