爲了方便知識的複習和查看,將其記錄下來,一塊兒努力吧!前端
涉及的面試題:什麼是淺拷貝?如何實現淺拷貝?什麼是深拷貝?如何實現深拷貝?面試
在說深淺拷貝以前,咱們須要瞭解一下不一樣數據類型的變量的存儲方式。數組
咱們都知道js的數據類型分爲兩大類:bash
基本數據類型異步
Number 、String、 Boolean、 Undefined、 Null、 Symbol函數
引用數據類型post
Object(Array/Rex)ui
基本數據類型保存在棧內存,引用類型保存在堆內存中。根本緣由在於保存在棧內存的必須是大小固定的數據,引用類型的大小不固定,只能保存在堆內存中,可是能夠把它的地址寫在棧內存中以供咱們訪問。spa
let eg1 = {
num:1,
obj: {
name:'gxm',
age:18
}
}
複製代碼
這個eg1
對象裏的屬性的儲存狀況以下:3d
淺拷貝只複製指向某個對象的指針,而不復制對象自己,新舊對象仍是共享同一塊內存。因此原對象和拷貝後的對象是相互影響的。
Object.assign
來解決問題。let real = {
str: '我是本體',
obj: {
name:'gxm',
age:18
}
}
let clone = Object.assign({}, real)
clone.str = '我克隆了';
console.log(real.str); //我是本體
console.log(clone.str); //我克隆了
clone.obj.age = 16 ;
console.log(real.obj.age); //16
console.log(clone.obj.age); //16
複製代碼
經過這個例子,咱們也能夠看出Object.assign()
只會拷貝全部屬性值到新的對象中,若是屬性值是對象的話,拷貝的是地址。因此Object.assign
不是深拷貝。
let a = {
age: 1
}
let b = {...a}
a.age = 2
console.log(b.age) // 1
複製代碼
一般淺拷貝就能解決大部分問題了,可是當咱們遇到以下狀況就須要使用到深拷貝了
let real = {
str: '我是本體',
obj: {
name:'gxm',
age:18
}
}
let clone = {...real}
clone.str = '我克隆了';
console.log(real.str); //我是本體
console.log(clone.str); //我克隆了
clone.obj.age = 16 ;
console.log(real.obj.age); //16
console.log(clone.obj.age); //16
複製代碼
淺拷貝只能解決了第一層的問題,若是接下去的值中還有對象的話,二者享有相同的引用。要解決這個問題,咱們須要引入深拷貝。
深拷貝就是拷貝多層,嵌套的對象也會被拷貝出來,至關於開闢一個新的內存地址用於存放拷貝的對象。
個人理解深拷貝就是徹底拷貝,且原對象和拷貝後的對象沒有任何關聯。
let realObj = {
name: 'gxm',
jobs: {
first: 'BAT'
}
}
let deepCloneObj = JSON.parse(JSON.stringify(realObj))
realObj.jobs.first = 'Google'
console.log(deepCloneObj.jobs.first) // BAT
複製代碼
可是該方法也是有侷限性的:
undefined
symbol
let obj = {
a: 1,
b: {
c: 2,
d: 3,
},
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)
複製代碼
若是你有這麼一個循環引用對象,你會發現你不能經過該方法深拷貝。
MessageChannel
深層拷貝MessageChannel
深層拷貝能夠用在含有有undefined
和循環引用的場景。
// 有undefined + 循環引用
let obj = {
a: 1,
b: {
c: 2,
d: 3,
},
f: undefined
}
obj.c = obj.b;
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
function deepCopy(obj) {
return new Promise((resolve) => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
deepCopy(obj).then((copy) => { // 請記住`MessageChannel`是異步的這個前提!
let copyObj = copy;
console.log(copyObj, obj)
console.log(copyObj == obj)
});
複製代碼
運行結果以下:
但拷貝有函數對象時,仍是會報錯。eg1: 這個例子能夠實現基本數據類型、引用對象類型和對象裏嵌套對象的深層拷貝,但不適用於循環引用。所謂循環引用就是本身調用本身。
function deepClone(obj) {
//obj是null的狀況
if(obj == undefined){
return obj;
}
//obj是基本數據類型的狀況
if(typeof obj !== 'object'){//此處的object必定要小寫
return obj;
}
//正則、時間
if(obj instanceof RegExp){
return new RegExp(obj);
}
if(obj instanceof Date){
return new Date(obj)
}
//若是obj是的數組或對象時
let cloneObj = new obj.constructor;//得到obj的構造函數
for(let key in obj ){
if(obj.hasOwnProperty(key)){
// cloneObj[key] = obj[key];
//這種狀況是obj[key]不是引用類型的值時,能夠直接使用,可是若是obj[key]是多層嵌套的引用類型呢?就使用以下狀況:
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
}
let arrObj = {
name:'gxm',
age:18,
friends:['cc','yy','mm','nn',['cc','xx',['LL','QQ']]],
others:undefined,
address:null
}
let newArrObj = deepClone(arrObj);
newArrObj.age = 20;
newArrObj.friends[4][0] = 'gg';
console.log('原對象------',arrObj);
console.log('新對象------',newArrObj);
複製代碼
運行結果以下:
原對象------
{ name: "gxm",
age: 18,
friends: ['cc','yy','mm','nn',['cc','xx',['LL','QQ']]],
others: undefined,
address: null
}
新對象------
{
name: "gxm",
age: 20,
friends: ['cc','yy','mm','nn',['gg','xx',['LL','QQ']]],
others: undefined,
address: null
}
複製代碼
eg2: 若是拷貝的對象中的屬性的值是循環引用的話,上面的深拷貝就不適用了,會崩掉的。要想解決這個問題,能夠採用hash表進行映射。
function deepClone(obj,hash = new WeakMap()) {
//hash爲了保存全部的數據,其實是使用WeakMap將數據暫存下來,能夠防止內存泄漏
//obj是null的狀況
if(obj == undefined){
return obj;
}
//obj是基本數據類型的狀況
if(typeof obj !== 'object'){
return obj;
}
//正則、時間
if(obj instanceof RegExp){
return new RegExp(obj);
}
if(obj instanceof Date){
return new Date(obj)
}
/*
* 第一次拷貝的時候hash表裏確定沒有,下面的判斷就不會執行。
* 以後若是hash表裏有這個對象,就return出來,下面的for循環就不會執行。
* 即若是已經拷貝過了該屬性,就不會再接着拷貝了,防止遞歸拷貝。
*/
if(hash.get(obj)){
return hash.get(obj);
}
//若是obj是的數組或對象時
let cloneObj = new obj.constructor;//得到obj的構造函數
//拷貝前和拷貝後進行對比
hash.set(obj , cloneObj);
for(let key in obj ){
if(obj.hasOwnProperty(key)){
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
let arrObj = {
name:'gxm',
age:18
}
arrObj.objj = arrObj;
let newArrObj = deepClone(arrObj);
console.log('原對象------',arrObj);
複製代碼
運行結果以下:
參考掘金小冊->前端面試之道
若是有誤,請留言!