摘要:所以,深拷貝是對對象以及對象的所有子對象進行拷貝實現方式就是遞歸調用淺拷貝對于深拷貝的對象,改變源對象不會對得到的對象有影響。
上一篇 JavaScript中的繼承前言
文章開始之前,讓我們先思考一下這幾個問題:
為什么會有淺拷貝與深拷貝
什么是淺拷貝與深拷貝
如何實現淺拷貝與深拷貝
好了,問題出來了,那么下面就讓我們帶著這幾個問題去探究一下吧!
如果文章中有出現紕漏、錯誤之處,還請看到的小伙伴多多指教,先行謝過
以下↓
數據類型在開始了解 淺拷貝 與 深拷貝 之前,讓我們先來回顧一下 JavaScript 的數據類型(可以參考這里 JavaScript中的數據類型)
在 JavaScript 中,我們將數據分為 基本數據類型(原始值) 與 引用類型
基本數據類型的值是按值訪問的,基本類型的值是不可變的
引用類型的值是按引用訪問的,引用類型的值是動態可變的
由于數據類型的訪問方式不同,它們的比較方式也是不一樣的
var a = 100; var b = 100; a === b // true var c = {a: 1, b: 2}; var d = {a: 1, b: 2}; c == d // false 兩個不同的對象
基本數據類型的比較是值得比較
引用類型的比較是引用地址的比較
鑒于以上數據類型的特點,我們可以初步想到:所謂 淺拷貝 與 深拷貝 可能就是對于值的拷貝和引用的拷貝(簡單數據類型都是對值的拷貝,不進行區分)
一般來說,我們所涉及的拷貝對象,也都是針對引用類型的。由于引用類型屬性層級可能也會有多層,這樣也就引出了我們所要去了解的 淺拷貝 與 深拷貝
淺拷貝顧名思義,所謂淺拷貝就是對對象進行淺層次的復制,只復制一層對象的屬性,并不包括對象里面的引用類型數據
想象一下,如果讓你自己去實現這個功能,又會有怎么的思路呢
首先,我們需要知道被拷貝對象有哪些屬性吧,然后還需要知道這些屬性都對應了那些值或者地址的引用吧。那么,答案已經呼之欲出了,是的,循環
var person = { name: "tt", age: 18, friends: ["oo", "cc", "yy"] } function shallowCopy(source) { if (!source || typeof source !== "object") { throw new Error("error"); } var targetObj = source.constructor === Array ? [] : {}; for (var keys in source) { if (source.hasOwnProperty(keys)) { targetObj[keys] = source[keys]; } } return targetObj; } var p1 = shallowCopy(person); console.log(p1)
在上面的代碼中,我們創建了一個 shallowCopy 函數,它接收一個參數也就是被拷貝的對象。
首先創建了一個對象
然后 for...in 循環傳進去的對象,為了避免循環到原型上面會被遍歷到的屬性,使用 hasOwnProperty 限制循環只在對象自身,將被拷貝對象的每一個屬性和值添加到創建的對象當中
最后返回這個對象
通過測試,我們拿到了和 person 對象幾乎一致的對象 p1。看到這里,你是不是會想那這個結果和 var p1 = person 這樣的賦值操作又有什么區別呢?
我們再來測試一波
var p2 = person; // 這個時候我們修改person對象的數據 person.name = "tadpole"; person.age = 19; person.friends.push("tt") p2.name // tadpole p2.age // 19 p2.friends // ["oo", "cc", "yy", "tt"] p1.name // tt p1.age // 18 p1.friends // ["oo", "cc", "yy", "tt"]
上面我們創建了一個新的變量 p2 ,將 person 賦值給 p2 ,然后比較兩個變量
-- | 和原數據是否指向同一對象 | 第一層數據為基本數據類型 | 原數據中包含子對象 |
---|---|---|---|
賦值 | 是 | 改變會使原數據一同改變 | 改變會使原數據一同改變 |
淺拷貝 | 否 | 改變不會使原數據一同改變 | 改變會使原數據一同改變 |
了解完淺拷貝,相信小伙伴們對于深拷貝也應該了然于胸了
淺拷貝由于只是復制一層對象的屬性,當遇到有子對象的情況時,子對象就會互相影響。所以,深拷貝是對對象以及對象的所有子對象進行拷貝
實現方式就是遞歸調用淺拷貝
function deepCopy(source){ if(!source || typeof source !== "object"){ throw new Error("error"); } var targetObj = source.constructor === Array ? [] : {}; for(var keys in source){ if(source.hasOwnProperty(keys)){ if(source[keys] && typeof source[keys] === "object"){ targetObj[keys] = source[keys].constructor === Array ? [] : {}; targetObj[keys] = deepCopy(source[keys]); }else{ targetObj[keys] = source[keys]; } } } return targetObj; } var obj1 = { arr: [1, 2, 3], key: { id: 22 }, func: function() { console.log(123) } } var obj2 = deepCopy(obj1); obj1.arr.push(4); obj1.arr // [1, 2, 3, 4] obj2.arr // [1, 2, 3] obj1.key === obj2.key // false obj1.func === obj2.func // true
對于深拷貝的對象,改變源對象不會對得到的對象有影響。只是在拷貝的過程中源對象的方法丟失了,這是因為在序列化 JavaScript 對象時,所有函數和原型成員會被有意忽略
還有一種實現深拷貝的方式是利用 JSON 對象中的 parse 和 stringify,JOSN 對象中的 stringify 可以把一個 js 對象序列化為一個 JSON 字符串,parse 可以把 JSON 字符串反序列化為一個 js 對象,通過這兩個方法,也可以實現對象的深復制
// 利用JSON序列化實現一個深拷貝 function deepCopy(source){ return JSON.parse(JSON.stringify(source)); } var o1 = { arr: [1, 2, 3], obj: { key: "value" }, func: function(){ return 1; } }; var o2 = deepCopy(o1); console.log(o2); // => {arr: [1,2,3], obj: {key: "value"}}實現拷貝的其他方式 淺拷貝
Array.prototype.slice()
Array.prototype.concat()
Object.assign
拓展操作符...
...
深拷貝很多框架或者庫都提供了深拷貝的方式,比如 jQuery 、 lodash 函數庫等等,基本實現方式也就和我們前面介紹的大同小異
后記根據需求的不同,比如有時候我們需要一個全新的對象,在修改它的時候不去影響到源對象,那么這個時候我們就可能需要深拷貝;反之,淺拷貝就能實現我們的需求
只是,我們需要注意到一點,那就是因為實現深拷貝使用遞歸的方式,就增加了性能的消耗
相信在不斷使用的過程中,你一定會對它越來越熟悉
最后,推薦一波前端學習歷程,不定期分享一些前端問題和有意思的東西歡迎 star 關注 傳送門
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/103879.html
摘要:所以,深拷貝是對對象以及對象的所有子對象進行拷貝實現方式就是遞歸調用淺拷貝對于深拷貝的對象,改變源對象不會對得到的對象有影響。 為什么會有淺拷貝與深拷貝什么是淺拷貝與深拷貝如何實現淺拷貝與深拷貝好了,問題出來了,那么下面就讓我們帶著這幾個問題去探究一下吧! 如果文章中有出現紕漏、錯誤之處,還請看到的小伙伴多多指教,先行謝過 以下↓ 數據類型在開始了解 淺拷貝 與 深拷貝 之前,讓我們先...
摘要:原文地址淺拷貝和深拷貝只針對像這樣的復雜對象的簡單來說,淺拷貝只拷貝一層對象的屬性,而深拷貝則遞歸拷貝了所有層級。淺拷貝通過來實現淺拷貝。 原文地址:http://www.silenceboy.com/201... 淺拷貝和深拷貝只針對像Object, Array這樣的復雜對象的.簡單來說,淺拷貝只拷貝一層對象的屬性,而深拷貝則遞歸拷貝了所有層級。 淺拷貝 通過 Object.ass...
摘要:引用數據類型是存放在堆內存中的,變量實際上是一個存放在棧內存的指針,這個指針指向堆內存中的地址。棧和堆的區別其實淺拷貝和深拷貝的主要區別就是數據在內存中的存儲類型不同。這里,對存在子對象的對象進行拷貝的時候,就是深拷貝了。 數據類型 在開始拷貝之前,我們從JavaScript的數據類型和內存存放地址講起。數據類型分為基本數據類型 和引用數據類型 基本數據類型主要包括undefin...
摘要:引用類型值引用類型值是保存在堆內存中的對象,變量保存的只是指向該內存的地址,在復制引用類型值的時候,其實只復制了指向該內存的地址。 前言 要理解 JavaScript中淺拷貝和深拷貝的區別,首先要明白JavaScript的數據類型。JavaScript有兩種數據類型,基礎數據類型和引用數據類型。js的基本類型:undefined,null,string,boolean,number,s...
摘要:拷貝到,屬性均順利拷貝。大輝小輝,小輝,大輝小輝,小輝,大輝但是,若修改的屬性變為對象或數組時,那么對象之間就會發生關聯。深拷貝不希望對象之間產生關聯,那么這時候可以用到深拷貝。 淺拷貝 之前文章提到,在定義一個對象或數組時,變量存放的往往只是一個地址。當我們對堆內存中的對象復制時,如果屬性是對象或數組時,這時候我們拷貝的只是一個棧內存的指針。因此b對象在訪問該屬性時,會根據指針尋找...
閱讀 3293·2021-11-18 10:02
閱讀 3454·2021-10-11 10:58
閱讀 3386·2021-09-24 09:47
閱讀 1134·2021-09-22 15:21
閱讀 3967·2021-09-10 11:10
閱讀 3287·2021-09-03 10:28
閱讀 1759·2019-08-30 15:45
閱讀 2156·2019-08-30 14:22