摘要:深拷貝淺拷貝本文主要對深拷貝淺拷貝的解釋及實現做一下簡單記錄。之所以會有深拷貝與淺拷貝之分,是因為不同數據類型的數據在內存中的存儲區域不一樣。但注意,只能做一層屬性的淺拷貝。
深拷貝VS淺拷貝
本文主要對深拷貝&淺拷貝的解釋及實現做一下簡單記錄。原文鏈接,歡迎star。
之所以會有深拷貝與淺拷貝之分,是因為不同數據類型的數據在內存中的存儲區域不一樣。
堆和棧是計算機中劃分出來用來存儲的區域,其中堆(heap)則是動態分配的內存,大小不定也不會自動釋放;而棧(stack)為自動分配的內存空間,它由系統自動釋放。存放在棧內存中的簡單數據段,數據大小確定,內存空間大小可以分配,是直接按值存放的,所以可以直接訪問。
眾所周知,JavaScript中的數據分為(基本類型和引用類型)。五種基本類型(boolean,number,undefined,null,string,)的數據的原始值是大小確定不可變的,所以放在棧內存中;而引用類型(object)是放在堆內存中的,對應賦值的變量只是一個存放在棧內存中的指針,也就是一個指向引用類型存放在堆內存中的地址。
引用類型比較與基本類型比較的區別引用類型是引用的比較,例如
// a與b是兩個引用類型,但其指針指向的是兩個不同的引用 let a = [1,2,3]; let b = [1,2,3]; a===b; //false // 引用類型的賦值操作,b的指針也是指向與a同樣的引用 let a = [1,2,3]; let b = a; a===b; //true
而基本類型間的比較是值的比較,例如
let a = 1; let b = 1; a===b; //true let a = 1; let b = a; a===b; //true賦值操作:傳值與傳址的區別
在對基本類型進行賦值操作的時候實際是傳值,即在棧內存中新開一個區域給新的變量,然后再把值賦給它。所以基本類型賦值的變量是兩個互相獨立變量。
let a = 10; let b = a; b++; console.log(a); //10 console.log(b); //11
而引用類型的賦值操作是傳址,只是在棧內存中新增了一個變量,同時賦值給這個變量的只是保存在堆內存中對象的地址,也就是指針的指向。因此這兩個變量的指針指向同一個地址,相互操作也就會有影響。
let a = {}; let b = a; a.name = "slevin"; console.log(b.name); //"slevin" console.log(a.name); //"slevin" console.log(a===b); //true
需要注意的是,引用類型的賦值并不算是淺拷貝,因為賦值操作只相當于是引用,兩個變量還是指向同一對象,任何一方改變了都會影響到另一方;但淺拷貝出來的變量與源變量已經不同,在不包含子對象的情況下(此情況即為深拷貝),一方改變不會影響到另一方。如下賦值操作:
let a = {}; let b = a; b.name = "slevin"; console.log(a.name); //"slevin";對b操作影響到了a console.log(a===b); //true;因為兩者指向同一個對象淺拷貝實現方法
自定義實現方法:
// 淺拷貝的方法 function shallowCopy(srcObj) { let copy = {}; for (let key in srcObj) { //只拷貝自身的屬性,__proto__上繼承來的屬性不做拷貝,也可去掉這個限制 if (srcObj.hasOwnProperty(key)) { copy[key] = srcObj[key]; } } return copy; } let obj = { name: "slevin", age: 18, language: { "english": "good", "mandarin": "wonderful", } } let copy = shallowCopy(obj); copy.age = 28; console.log(obj.age); // 18; 并沒有改變源對象 console.log(obj === copy); //false;兩者指向不是同一個對象
利用Object.assign(target,...sources)可將一個或多個源對象上可枚舉屬性的值復制到目標對象,并返回目標對象。但注意,只能做一層屬性的淺拷貝。
let obj = { name: "slevin", age: 18, language: { english: "good", mandarin: "wonderful", test: [1, 2, 3] }, fn:function(){ console.log("this:",this.name); } } let copy = Object.assign({},obj); //不會改變源對象 copy.age = 22; //會改變源對象 copy.language.english = "bad"; console.log("copy===obj:",copy===obj);//false console.log("copy:",copy); console.log("obj:",obj);
對于數組來說,也可以使用Array.prototype.slice()方法和Array.prototype.concact()方法
let obj = [1,2,["a","b","c"]] let copy = obj.slice(); // 不會改變源對象 copy[0]=111; // 會改變源對象 copy[2][0]="aaa"; console.log("copy===obj:",copy===obj);//false console.log("copy:",copy); console.log("obj:",obj);深拷貝及實現方法
簡單來說,深拷貝就是把對象以及對象的子對象進行拷貝。因為淺拷貝只復制了一層對象的屬性,而如果對象的數值也為引用類型,那么拷貝過來依然是個引用地址,在拷貝對象上對子對象的值進行操作會改變原始數據中的值。
例如上面obj淺拷貝得到copy對象,如果在copy對象上改變子對象copy.language上的屬性值,會影響到源對象obj:
copy.language.english = "bad"; copy.language.mandarin = "bad"; console.log(obj.language); // {"english": "bad","mandarin": "bad",}
那么如何深拷貝呢?思路就是遞歸調用剛剛的淺拷貝,遍歷所有值為引用類型的屬性,然后依次賦值給另外一個對象即可。
方法一,自定義實現:
/**將源對象source深度合并到目標對象target上
source 源對象
target 目標對象,默認{}
deep 是否深度合并,默認true
*/
function deepCopy (source,target={},deep=true) { for (let key in source){ // 深拷貝,而且只拷貝自身屬性.(默認傳入的source為對象) if (deep && source.hasOwnProperty(key)){ if (typeof(source[key])==="object"){ // // source[key] 是對象,而 target[key] 不是對象, 則 target[key] = {} 初始化一下,否則遞歸會出錯的 // if (!(target[key] instanceof Object) && (source[key] instanceof Object)){ // target[key] = {}; // } // // source[key] 是數組,而 target[key] 不是數組,則 target[key] = [] 初始化一下,否則遞歸會出錯的 // if (!Array.isArray(target[key]) && Array.isArray(source[key])) { // target[key] = []; // } // 上面的代碼可以簡化為以下: target[key] = Array.isArray(source[key]) ? [] : {}; // 遞歸執行拷貝 deepCopy(source[key],target[key],true); } else { target[key] = source[key]; } } } return target; } let obj = { name: "slevin", age: 18, language: { english: "good", mandarin: "wonderful", test:[1,2,3] } } let copy = deepCopy(obj); copy.language.test[0] = 111; console.log("copy:",copy); //111 改變目標對象的子對象屬性值 console.log("obj:",obj); //1 對應源對象上沒有改變
利用JSON.parse(JSON.stringify(copyObj))方法
let obj = { name: "slevin", age: 18, language: { english: "good", mandarin: "wonderful", test: [1, 2, 3] } } let copy = JSON.parse(JSON.stringify(obj)) copy.language.test[0]=111; console.log("copy===obj:",copy===obj);//false console.log("copy.language.test[0]:",copy.language.test[0]);//111 console.log("obj.language.test[0]:",obj.language.test[0]);//1
但要注意,此方法有兩個缺點:
如果源對象屬性值有函數,無法拷貝下來
無法拷貝源對象原型鏈上的屬性和方法
let obj = { name: "slevin", age: 18, language: { english: "good", mandarin: "wonderful", test: [1, 2, 3] }, fn:function(){ console.log("this:",this.name); } } let copy = JSON.parse(JSON.stringify(obj)); console.log("copy===obj:",copy===obj);//false console.log("obj.fn:",obj.fn); //fn(){}... console.log("copy.fn:",copy.fn); //undefined
最后,再補一張引用類型的賦值操作、淺拷貝深拷貝對比圖,加深印象
參考資料js 深拷貝 vs 淺拷貝
深拷貝與淺拷貝的區別,實現深拷貝的幾種方法
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101561.html
摘要:而在這個運算符的相關用例中,往往會涉及到其他知識點,深拷貝和淺拷貝就是其中之一。即對象的淺拷貝會對主對象的值進行拷貝,而該值有可能是一個指針,指向內存中的同一個對象。,可以看到深拷貝和淺拷貝是對復制引用類型變量而言的。 在ES6的系列文章中,基本都會提到Spread——擴展運算符(...)。而在這個運算符的相關用例中,往往會涉及到其他知識點,深拷貝和淺拷貝就是其中之一。 背景知識 在討...
摘要:對象的淺拷貝淺拷貝是對象共用一個內存地址,對象的變化相互影響。這是特別值得注意的地方。和能正確處理的對象只有等能夠被表示的數據結構,因此函數這種不能被表示的類型將不能被正確處理。 對象的淺拷貝: 淺拷貝是對象共用一個內存地址,對象的變化相互影響。比如常見的賦值引用就是淺拷貝: let srcObj = {name: lilei, age: 20}; let copyObj = srcO...
摘要:針對于的對象和數組數組也是對象淺拷貝只是引用,內存不變而深拷貝就是遞歸賦值。而淺拷貝會影響還可以用一句簡單的代碼實現上面的深拷貝 針對于JavaScript的對象和數組(數組也是對象)淺拷貝只是引用,內存不變;而深拷貝就是遞歸賦值。 深拷貝是不同內存,相互獨立。而淺拷貝會影響 var arr = [1,2,3],arr2=[]; for(var i=0;i
摘要:什么是深拷貝淺拷貝見名知義,無論是深拷貝還是淺拷貝,都是的問題。使用如下以上就是關于中的深拷貝與淺拷貝的知識和如何進行深拷貝的知識了,如果有錯或者有其他方式的話,歡迎在下面留言評論啦 前言 最近在寫項目的時候涉及到一些父子組件傳遞個對象或者數組通信啥的,或者是直接復制添加對象啥的,直接使用賦值的時候總會出錯。一查原來是淺拷貝的問題,就從網上找了點資料,匯總到這里來了。 1 什么是深拷貝...
摘要:案例中的賦值就是典型的淺拷貝,并且深拷貝與淺拷貝的概念只存在于引用類型。修改修改經測試,也只能實現一維對象的深拷貝。經過驗證,我們發現提供的自有方法并不能徹底解決的深拷貝問題。 在說深拷貝與淺拷貝前,我們先看兩個簡單的案例: //案例1 var num1 = 1, num2 = num1; console.log(num1) //1 console.log(num2) //1 num...
閱讀 2045·2023-04-26 02:23
閱讀 1789·2021-09-03 10:30
閱讀 1351·2019-08-30 15:43
閱讀 1191·2019-08-29 16:29
閱讀 530·2019-08-29 12:28
閱讀 2332·2019-08-26 12:13
閱讀 2169·2019-08-26 12:01
閱讀 2400·2019-08-26 11:56