爲何會有淺拷貝與深拷貝
什麼是淺拷貝與深拷貝
如何實現淺拷貝與深拷貝
好了,問題出來了,那麼下面就讓咱們帶着這幾個問題去探究一下吧!框架
若是文章中有出現紕漏、錯誤之處,還請看到的小夥伴多多指教,先行謝過函數
如下↓性能
數據類型
在開始瞭解 淺拷貝 與 深拷貝 以前,讓咱們先來回顧一下 JavaScript 的數據類型(能夠參考這裏 JavaScript中的數據類型)測試
在 JavaScript 中,咱們將數據分爲 基本數據類型(原始值) 與 引用類型spa
基本數據類型的值是按值訪問的,基本類型的值是不可變的
引用類型的值是按引用訪問的,引用類型的值是動態可變的
因爲數據類型的訪問方式不一樣,它們的比較方式也是不同的prototype
var a = 100; var b = 100; a === b // true var c = {a: 1, b: 2}; var d = {a: 1, b: 2}; c == d // false 兩個不一樣的對象
·基本數據類型的比較是值得比較
·引用類型的比較是引用地址的比較code
鑑於以上數據類型的特色,咱們能夠初步想到:所謂 淺拷貝 與 深拷貝 可能就是對於值的拷貝和引用的拷貝(簡單數據類型都是對值的拷貝,不進行區分)對象
通常來講,咱們所涉及的拷貝對象,也都是針對引用類型的。因爲引用類型屬性層級可能也會有多層,這樣也就引出了咱們所要去了解的 淺拷貝 與 深拷貝blog
淺拷貝遞歸
顧名思義,所謂淺拷貝就是對對象進行淺層次的複製,只複製一層對象的屬性,並不包括對象裏面的引用類型數據
想象一下,若是讓你本身去實現這個功能,又會有怎麼的思路呢
首先,咱們須要知道被拷貝對象有哪些屬性吧,而後還須要知道這些屬性都對應了那些值或者地址的引用吧。那麼,答案已經呼之欲出了,是的,循環
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 函數庫等等,基本實現方式也就和咱們前面介紹的大同小異
後記
根據需求的不一樣,好比有時候咱們須要一個全新的對象,在修改它的時候不去影響到源對象,那麼這個時候咱們就可能須要深拷貝;反之,淺拷貝就能實現咱們的需求
只是,咱們須要注意到一點,那就是由於實現深拷貝使用遞歸的方式,就增長了性能的消耗
相信在不斷使用的過程當中,必定會對它愈來愈熟悉