Js復制物件/克隆物件 Js淺拷貝與深拷貝 淺拷貝和深拷貝的實作方法
前言
學習Js克隆一個物件,作為準備作業,需要理解Js中的資料型別和按值傳遞:Js中的資料型別和按值傳遞
淺拷貝最后兩種方法涉及到了繼承和es5新特性中的call方法,可以讀es5替換函式中的this的方法
Js中的prototype、__proto__和constructor
1. 淺拷貝
1.1. 賦值和淺拷貝
概念:
淺拷貝是按位拷貝物件,它會創建一個新物件,這個物件有著原始物件屬性值的一份精確拷貝,如果屬性是原始型別,拷貝的就是原始型別的值;如果屬性是參考型別,拷貝的就是記憶體地址 ,
代碼示例:
var student={
name:"Lily",
age:15,
sex:"女",
friends:["Jack","Rose","Ben"]
}
var obj1=student;
function clone(obj){
var newObj={};
for(var key in obj){
newObj[key]=obj[key]
}
return newObj;
}
var obj2=clone(student)
obj1.name="Tom";
obj2.sex="男";
obj2.friends[0]="Lilei";
console.log(student.name) //Tom
console.log(student.age) //15
console.log(student.friends) //["Lilei", "Rose", "Ben"]
決議:
i. 賦值:
上面一段代碼,student變數中保存著所創建物件的地址,obj1由賦值得到,根據按值傳遞,我們知道,obj1變數中保存的是student所存地址的副本,這兩個地址指向同一個實體化物件,無論哪個物件發生改變,都會改變這個實體化物件,兩個物件是聯動的,
ii. 淺拷貝:
a. 重新創建一個新物件,逐個拷貝源物件的屬性;
b. 如果屬性值是原始型別資料,拷貝的是原始型別的值的副本,原始型別的屬性在新物件和源物件之間互不影響
c. 如果屬性值是參考型別資料,拷貝的是地址,如果其中一個的地址發生改變,就會影響到另外一個物件
1.2. 淺拷貝的方法
1.2.1. Object.assign()
Object.assign()方法可以將源物件自身的可列舉屬性(任意多個)拷貝給目標物件,然后回傳目標物件
var student={
name:"Lily",
age:15,
sex:"女",
friends:["Jack","Rose","Ben"]
}
var obj3=Object.assign({},student)
console.log(obj3.name) //LilY
1.2.2. 展開運算子…(Es6新特性)
var student={
name:"Lily",
age:15,
sex:"女",
friends:["Jack","Rose","Ben"]
}
var obj4={...student}
console.log(obj4.name) //LilY
1.2.3. 強行呼叫陣列的concat方法
var student={
name:"Lily",
age:15,
sex:"女",
friends:["Jack","Rose","Ben"]
}
var obj5=Array.prototype.concat.call({},student)
console.log(obj5) //{name: "Lily", age: 15, sex: "女", friends: Array(3)}
1.2.4. 強行呼叫陣列的slice方法
var student={
name:"Lily",
age:15,
sex:"女",
friends:["Jack","Rose","Ben"]
}
var obj6=Array.prototype.slice.call({},student)
console.log(obj6) //{name: "Lily", age: 15, sex: "女", friends: Array(3)}
2.深拷貝
概念
深拷貝是將一個物件從記憶體中完整的拷貝出一份,從記憶體中開辟出一個新的區域來存放新物件,新物件跟原物件不共享記憶體,修改新物件不會影響原物件,
2.1. 深拷貝與淺拷貝對比

2.2. 深拷貝的實作方式
2.2.1. JSON.parce(JSON.stringify())
var student={
name:"Lily",
age:15,
sex:"女",
friends:["Jack","Rose","Ben"]
}
var obj7=JSON.parse(JSON.stringify(student))
console.log(obj7) //{name: "Lily", age: 15, sex: "女", friends: Array(3)}
obj7.friends[0]="Alice"
console.log(student.friends[0]) //Jack 未影響源物件
利用JSON.stringify將物件轉成JSON字串,再用JSON.parse把字串決議成物件,一去一來,新的物件產生了,而且物件會開辟新的記憶體空間,實作深拷貝,
這種方法雖然可以實作陣列或物件深拷貝,但不能處理函式和正則,因為這兩者基于JSON.stringify和JSON.parse處理后,JSON.stringify()方法在處理undefined、任意的函式以及 symbol 值時,作為非陣列物件的屬性值時會被忽略,作為陣列物件中的屬性值時,會被轉換成 null,函式、undefined 被單獨轉換時,會回傳 undefined,
使用JSON.parce(JSON.stringify())克隆非陣列物件,源物件中的方法被忽略
var student={
name:"Lily",
age:15,
sex:"女",
friends:["Jack","Rose","Ben"],
say:function(){
console.log("hello")
}
}
var obj7=JSON.parse(JSON.stringify(student))
console.log(obj7) //{name: "Lily", age: 15, sex: "女", friends: Array(3)} say方法被忽略
使用JSON.parce(JSON.stringify())克隆陣列物件,源物件中的方法被轉換成null
var arr= [1,2,3,function say(){console.log("hello")}];
var obj=JSON.parse(JSON.stringify(arr));
console.log(arr,obj)

這里列舉了一些最常用情況下的弊端,其他方面的弊端可參考JSON.stringify()
2.2.2.手寫遞回
遞回方法實作深度克隆原理:遍歷物件直到最內層都是原始資料型別,然后再去復制,就是深度拷貝,
有種特殊情況需注意就是物件存在回圈參考的情況,即物件的屬性直接的參考了自身的情況,解決回圈參考問題,我們可以額外開辟一個存盤空間,來存盤當前物件和拷貝物件的對應關系,當需要拷貝當前物件時,先去存盤空間中找,有沒有拷貝過這個物件,如果有的話直接回傳,如果沒有的話繼續拷貝,
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj;
// 如果是null或者undefined我就不進行拷貝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是物件或者普通的值 如果是函式的話是不需要深拷貝
if (typeof obj !== "object") return obj;
// 是物件的話就要進行深拷貝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所屬類原型上的constructor,而原型上的 constructor指向的是當前類本身
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 實作一個遞回拷貝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
let obj = { name: 1, address: { x: 100 } };
obj.o = obj; // 物件存在回圈參考的情況
let d = deepClone(obj);
obj.address.x = 200;
console.log(d);
決議:
WeakMap 物件是一組鍵/值對的集合,其中的鍵是弱參考的,其鍵必須是物件,而值可以是任意的,
方法:
WeakMap.prototype.delete(key)
移除key的關聯物件,執行后 WeakMap.prototype.has(key)回傳false,
WeakMap.prototype.get(key)
回傳key關聯物件, 或者 undefined(沒有key關聯物件時),
WeakMap.prototype.has(key)
根據是否有key關聯物件回傳一個Boolean值,
WeakMap.prototype.set(key, value)
在WeakMap中設定一組key關聯物件,回傳這個 WeakMap物件
參考文章:
如何寫出一個驚艷面試官的深拷貝?(找不到文章的網址,只能貼個題目)
一文讀懂 javascript 深拷貝與淺拷貝(公眾號推文,找不到文章的網址,只能貼個題目)
WeakMap 物件
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/82769.html
標籤:其他
