前言
什麼是深複製與淺複製?深複製與淺複製是js中處理物件或資料複製操作的兩種方式。在聊深淺複製之前咱得了解一下js中的兩種資料型別:
基本資料型別(6種)
String、Number、Object、Boolean、null、undefined、symbol(ES6+)
引用資料型別
Object(function、Array、正規表示式等皆是物件)
- 資料的儲存方式是什麼?
基本資料: 基本資料型別是存放在棧中的簡單資料段,它們是直接按值存放的,所以可以直接按值訪問
引用型別: 引用型別是存放在堆記憶體中的物件,儲存的在棧記憶體中的一個指標,儲存的是棧記憶體中物件在堆記憶體中的引用地址。透過這個引用地址可以快速查詢到儲存中堆記憶體中的物件。
1.淺複製
1.1 什麼是淺複製
淺複製,指的是建立新的資料,這個資料有著原始資料屬性值的一份精確複製。如果屬性是基本型別,複製的就是基本型別的值。如果屬性是引用型別,複製的就是記憶體地址即淺複製是複製一層,深層次的引用型別則共享記憶體地址。
- 下面用一張圖來解釋一下淺複製
1.2 淺複製實現方法
1.2.1 assign
var obj = {
age: 18,
person: {
name1: 'fx',
name2: 'xka'
},
list:['hhh','666'],
love: function () {
console.log('嘿嘿')
}
}
var newObj = Object.assign({}, obj);
//因為是淺複製,所以只複製了基本型別的,引用型別還是共享記憶體地址的,即改變obj的應用型別的內容,newObj裡面的引用型別的值也隨之改變
obj.person.name1='xxx'
obj.list[0]='xxx'
console.log(newObj.person.name1) //xxx
1.2.2 slice
const fxArr = ["One", {
name: "Two",
age: 20
}, "Three"]
const fxArrs = fxArr.slice(0,)
fxArr[1].name = "four";
console.log(fxArrs[1].name) //four
1.2.3 concat
const fxArr = ["One", {
name: "Two",
age: 20
}, "Three"]
const fxArrs = fxArr.concat()
fxArr[1].name = "four";
console.log(fxArrs[1].name) //four
1.2.4 擴充運算子
const fxArr = ["One", {
name: "Two",
age: 20
}, "Three"]
const fxArrs = [...fxArr]
fxArr[1].name = "four";
console.log(fxArrs[1].name) //four
2.深複製
2.1 什麼是深複製
深複製開闢一個新的棧,兩個物件屬完成相同,但是對應兩個不同的地址,修改一個物件的屬性,不會改變另一個物件的屬性
- 下面用一張圖來解釋一下深複製
2.2 淺複製實現方法
2.2.1 JSON.parse(常用)
var obj = {
age: 18,
person: {
name1: 'fx',
name2: 'xka'
},
list:['hhh','666'],
love: function () {
console.log('嘿嘿')
}
}
const obj2=JSON.parse(JSON.stringify(obj));
obj.person.name1='6666'
console.log(obj2.person.name1) //fx
- 我常用的基本就是JSON.parse了,然而其他的,之前聽過的lodash的cloneDeep,jq的extend我都沒使用過。
- 但是適用JSON.parse會有一個缺點,就是處理的資料裡面有undefined、function、symbol會被忽略,但是這也是一個優點,可以利用其特性將undefined等資料排除,拿到乾淨的資料。還有一個缺點就是在處理的資料比較大的話,還有效能問題。
3.手寫實現深淺複製
3.1 淺複製
function clone(object){
const newObj={}
for(let proto in object){
if(object.hasOwnProperty(proto)){
newObj[proto]= object[proto]
}
}
return newObj
}
var obj = {
age: 18,
person: {
name1: 'fx',
name2: 'xka'
},
list:['hhh','666'],
love: function () {
console.log('嘿嘿')
}
}
const obj1=clone(obj)
console.log(obj)
console.log(obj1)
3.2 深複製
// 手寫深複製
function deepClone(obj, hash = new WeakMap()) {
// 資料過濾
if (obj === null) return obj; // 如果是null或者undefined我就不進行複製操作
if (obj instanceof Date) return new Date(obj);// 如果傳入的物件是日期物件,使用 new Date() 建立一個新的日期物件並返回
if (obj instanceof RegExp) return new RegExp(obj);// 如果傳入的物件是正規表示式物件,使用 new RegExp() 建立一個新的正規表示式物件並返回
// 如果傳入的物件不是普通物件(即不是物件型別)或普通的值 如果是函式的話就不需要深複製
// 因為複製的只需要考慮是否為物件,所以只需要判斷obj是否為物件型別即可,因為null或者undefined在上面已經先過濾掉了,此時就只剩下基本資料型別和函式了
if (typeof obj !== "object") return obj;
// 來到此處的話就只剩下物件了,就要進行深複製
if (hash.get(obj)) return hash.get(obj);
// 深複製
// 建立一個新物件,這個新物件和obj物件型別相同
// 找到的是所屬類原型上的constructor屬性,而原型上的constructor指向的是當前類本身
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 實現一個遞迴複製
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
var obj = {
age: 18,
person: {
name1: 'fx',
name2: 'xka'
},
list:['hhh','666'],
love: function () {
console.log('嘿嘿')
}
}
const obj2 = deepClone(obj) // 深複製
const obj3 = Object.assign({}, obj) // 淺複製
const obj4 = clone(obj) // 淺複製
obj.person.name1 = 'hhh';
//因為是深複製,obj2中的引用型別新開闢了一個記憶體地址,所以obj的person改變obj2不受影響
console.log(obj2.person.name1) //fx
//因為是淺複製,obj3、obj4中的引用型別與obj中的引用型別共享記憶體地址,所以obj的person改變obj3、obj4皆受影響
console.log(obj3.person.name1) //hhh
console.log(obj4.person.name1) //hhh
上述為個人學習整理內容,水平有限,如有錯誤之處,望各位園友不吝賜教!如果覺得不錯,請點選推薦和關注!謝謝~๑•́₃•̀๑ [鮮花][鮮花][鮮花]