一、前言
hello,大家好~ ,本文主要介紹在 JavaScript 中什么是深拷貝和淺拷貝,以及如何實作一個物件的深拷貝,
二、隨處可見的 “賦值”
在 JavaScript 中我們最常見的操作之一是將一個變數的值賦值給另一個變數,這個程序我們也可以稱為 “拷貝” 一份變數的值給另一個變數,
2.1 基本資料型別的賦值操作
在涉及到基本資料型別(string、Boolean、number...)賦值操作的時候,原始變數值被復制給另外一個變數,我們來考慮下下面這段代碼:我們將值為 10 的變數 x 賦值給變數 y,此時 x 和 y 已經失去“聯系”了,所以改變 x 的值不會影響 y 的值,最后輸出 20 和 10,
<script>
let x = 10;
let y = x;
x += 10;
console.log(x, y); // 20 10
</script>
2.2 參考資料型別的賦值操作
與基本資料型別賦值相反,參考資料型別(Object、Array...)在賦值的時候傳遞的只是一個 參考 (原始值和拷貝賦值后的值指向同一塊記憶體空間) ,所以當被復制出來的物件值改變的時候,原始的物件值也會跟著改變,這種現象被稱為 淺拷貝 ,
物件賦值案例: 將 user 物件賦值給 user2 變數,修改 user2 屬性值后 user 屬性值也會跟著變,兩者的值始終保持同步,這便是淺拷貝帶來的 副作用 ,拷貝出來的值和原始值始終保持著關聯,基本資料型別的賦值沒有這種現象,所以說 淺拷貝 和 深拷貝 是相對參考資料型別而言的,
<script>
let user = {
name: 'zjl712',
age: 18,
local: 'shanghai'
}
let user2 = user;
user2.name = 'zjl';
console.log('user:', user, 'user2:', user2);
</script>

三、實作物件的深拷貝
通過以上的介紹我們知道什么是 淺拷貝 以及淺拷貝帶來的 副作用 ,為了消除這種“副作用”我們需要對物件進行 深拷貝 ,下面將介紹一些物件拷貝的方法,它們各有優缺點,
- "=" 號直接賦值
通過等號(=)將原始值賦值給一個新的值,優點是簡便快捷,缺點 是只能進行 淺拷貝
let obj = {name:'zjl712', age:18}
let obj2 = obj;
console.log(obj == obj2) //true
- Object.assign()
通過 Object 的 assign 方法可以對物件進行深拷貝,缺點是不能對含有多層嵌套結構的物件進行深拷貝,
深拷貝單層結構的物件,改變拷貝物件的值不會改變原始物件的值,
<script>
let obj = {
name: 'zjl712',
age: 18,
getName:function(){
return this.name
}
}
let obj2 = Object.assign({}, obj)
console.log(obj == obj2); // false
obj2.name = 'zjl'
console.log(obj, obj2);
</script>

嘗試拷貝多層嵌套結構物件,改變拷貝物件內嵌套的物件值(obj2.local.nickname)后,原始物件嵌套的值也會跟著改變,改變拷貝物件非嵌套的值,原始物件的值不會跟著變,所以 Object.assign() 只能實作部分深拷貝,
<script>
let obj = {
name: 'zjl712',
age: 18,
getName:function(){
return this.name
},
local: {
name: 'shanghai',
nickname: 'modu',
}
}
let obj2 = Object.assign({}, obj)
console.log(obj == obj2); // false
obj2.local.nickname = '魔都'
obj2.name = 'zjl'
console.log(obj, obj2);
console.log(obj.local == obj2.local) // true
</script>

- JSON.stringify() 和 JSON.parse()
JSON.stringify() 方法接收一個物件作為引數,將物件轉為 JSON 字串,JSON.parse() 接收一個 JSON 字串,將該字串轉為一個物件,結合這兩個方法可以對一個物件進行深拷貝,
結合以上兩個方法對物件進行深拷貝
let obj = {name: 'zjl712',age: 18}
let obj2 = JSON.parse(JSON.stringify(obj))
console.log(obj == obj2); // false
obj2.name = 'zjl'
console.log(obj, obj2);

當物件屬性值含有 函式(function) 時,且含有嵌套物件時,拷貝的值 屬性值為函式的屬性會丟失 ,優點是能對多層嵌套物件進行深拷貝,
let obj = {
name: 'zjl712',
age: 18,
getName:function(){
return this.name
},
local: {
name: 'shanghai',
nickname: 'modu',
}
}
let obj2 = JSON.parse(JSON.stringify(obj))
console.log(obj == obj2); // false
obj2.local.nickname = '魔都'
obj2.name = 'zjl'
console.log(obj, obj2);
console.log(obj.local == obj2.local) // false

- 物件擴展符(...)
使用 ES6 的擴展運算子,使用三個點(...)將原始物件的值拷貝給另一個物件,然而三點擴展符和 Object.assign() 很相似,擴展符不能對多層嵌套物件進行深拷貝,
let obj = {
name: 'zjl712',
age: 18,
getName:function(){
return this.name
},
local: {
name: 'shanghai',
nickname: 'modu',
}
}
let obj2 = {...obj}
console.log(obj == obj2); // false
obj2.local.nickname = '魔都'
obj2.name = 'zjl'
console.log(obj, obj2);

- 遞回的方式實作物件的深拷貝
實作含有嵌套結構、函式物件的深拷貝以上方法都無能為力,我們可以用遞回的方法完成物件的深拷貝,缺點是對復雜的物件(Buffer、Date 等實體物件)無法實作深拷貝
function myDeepClone(obj){
let clone;
// 排除非參考型別資料
if(obj == null || typeof obj != 'object') return obj;
if(Array.isArray(obj)){
// obj 是陣列
clone = new obj.constructor(obj.length)
obj.forEach((value, index) => {
clone[index] = typeof value =https://www.cnblogs.com/zjl-712/p/=='object'?myDeepClone(value):value
})
}else{
// 淺拷貝一份原始資料
clone = Object.assign({}, obj)
// 遞回 clone 內的每一個屬性值
Object.keys(clone).forEach(key => {
clone[key] = typeof obj[key] === 'object'?myDeepClone(obj[key]):obj[key]
})
}
return clone;
}
以上遞回代碼有相似代碼(兩個 forEach 內的代碼),可簡化為以下代碼:
function myDeepClone(obj){
let clone;
if(obj == null || typeof obj != 'object') return obj;
clone = Object.assign({}, obj)
Object.keys(clone).forEach(key => {
clone[key] = typeof obj[key] === 'object'?myDeepClone(obj[key]):obj[key]
})
if(Array.isArray(obj)){
clone.length = obj.length
clone = Array.from(clone)
}
return clone
}
- 以上的自定義遞回方法不夠全面,但對于基本的物件( {} )和陣列( [] )深拷貝還是夠用的,要想實作復雜物件的深拷貝可以使用 lodash 的 cloneDeep 方法,這個方法實作深拷貝是比較完美的,
// 安裝 lodash :npm i lodash
// 使用
let lang = require('lodash/lang')
let clone = lang.cloneDeep(obj)
路很遠,但從未止步
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/535219.html
標籤:JavaScript
上一篇:5 分鐘速通 SVG
