一、淺拷貝
1. 單個物件複製
/**
* 實現單個物件複製 - 淺拷貝
* @param obj
* @returns {Array}
*/
function clone(obj) {
const newObj = obj instanceof Array ? [] : typeof obj === 'object' && typeof to !== 'function' ? {} : '';
if (!newObj) throw new Error('obj is not object');
for (let key in obj) {
if (obj.hasOwnProperty(key)) newObj[key] = obj[key];
}
return newObj;
}
複製程式碼
Demo:
let obj1 = {
name: '張三',
age: 12,
language: {
zh: '中文',
en: '英文'
},
color:['red','yellow']
};
let newObj = clone(obj1);
console.log(newObj);
//'{"name":"張三","age":12,"language":{"zh":"中文","en":"英文"},"color":["red","yellow"]}'
obj1.language.be = '白俄羅斯';
console.log(newObj);
//'{"name":"張三","age":12,"language":{"zh":"中文","en":"英文","be":"白俄羅斯"},"color":["red","yellow"]}'
複製程式碼
2. 兩個物件合併
/**
* 實現兩個物件合併 - 淺拷貝
* @param to
* @param from
* @returns {*}
*/
function extend(to, from) {
if ( typeof to !== 'object' || typeof to === 'function'){
throw new Error('to is not object')
}
if ( typeof from !== 'object' || typeof from === 'function'){
throw new Error('from is not object')
}
for (let key in from) {
if (from.hasOwnProperty(key)) to[key] = from[key];
}
return to;
}
複製程式碼
Demo:
let obj1 = {
name: '張三',
age: 12,
language:{
zh:'中文',
en:'英文'
}
};
let obj2 = {
name: '李四',
age: 22,
language:{
other:'其他'
}
};
const options = extend(obj1, obj2);
console.log(options);
//'{"name":"李四","age":22,"language":{"zh":"中文","en":"英文","be":"白俄羅斯文"}}'
obj2.language.be = '白俄羅斯文';
console.log(options);
//'{"name":"李四","age":22,"language":{"zh":"中文","en":"英文","be":"白俄羅斯文"}}'
複製程式碼
淺複製有個問題,就是隻能複製物件的第一層,更深的層次只是物件的引用。
二、深拷貝
1. 單個物件複製
從基礎版優化到至尊版
/**
* 實現物件深拷貝 - 基礎版
* @param obj
*/
function deepClone(obj) {
deepClone.loop = function(to,from){
// 迴圈源物件
for (let key in from) {
// 1.判斷源物件的屬性值是否為陣列
// 2.如果沒有這個屬性, 則需要建立個屬性, 且值為空陣列
if (from[key] instanceof Array && !to[key]) to[key] = [];
// 判斷源物件的屬性值是否為物件, 包括普通物件、陣列物件
if (typeof from[key] === 'object') {
// 判斷目標物件是否有這個屬性, 沒有則建立
if (!to[key]) to[key] = {};
// 如果判斷是物件型別, 則重新呼叫自己, 這就是大家所說的遞迴。
deepClone.loop(to[key],from[key]);
} else {
// 如果源物件是否為陣列物件, 如果是直接push追加, 如果不是則新建屬性直接賦值
if (from instanceof Array) {
to.push(from[key])
} else {
to[key] = from[key];
}
}
}
return to;
};
return deepClone.loop({},obj);
}
/**
* 實現物件深拷貝 - 升級版
* @param obj
* @returns {*}
*/
function deepClone(obj) {
const newObj = obj instanceof Array ? [] : {};
// 迴圈源物件
for (let key in obj) {
// 判斷源物件的屬性值是否為物件, 包括普通物件、陣列物件
if (typeof obj[key] === 'object') {
// 判斷目標物件是否有這個屬性, 沒有則建立
newObj[key] = deepClone(obj[key]);
} else {
newObj[key] = obj[key];
}
}
return newObj;
}
/**
* 實現物件深拷貝 - 至尊版
* @param obj
* @returns {*}
*/
function deepClone(obj) {
const newObj = obj instanceof Array ? [] : typeof obj === 'object' && typeof to !== 'function' ? {} : '';
if (!newObj) throw new Error('obj is not object');
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
typeof obj[key] === 'object' ? newObj[key] = deepClone(obj[key]) : newObj[key] = obj[key];
}
}
return newObj;
}
複製程式碼
其他實現方式,最簡單的方法就是用 JSON.stringify()
方法。
let obj1 = {
name: '張三',
age: 12,
language: {
zh: '中文',
en: '英文'
},
color:['red','yellow']
};
let newObj = JSON.parse(JSON.stringify(obj1)); //就是這麼簡單
複製程式碼
2. 兩個物件合併
修改下之前淺複製的程式碼:
/**
* 實現兩個物件合併 - 深拷貝
* @param to
* @param from
* @returns {*}
*/
function extend(to, from) {
// 判斷是否為物件, 如果不是則丟擲錯誤
if (typeof to !== 'object' || typeof to === 'function') {
throw new Error('to is not object')
}
// 判斷是否為物件, 如果不是則丟擲錯誤
if (typeof from !== 'object' || typeof from === 'function') {
throw new Error('from is not object')
}
// 迴圈源物件
for (let key in from) {
if (typeof from[key] === 'object') {
// 1.如果是陣列, 再判斷目標物件有沒這個屬性
// 2.如果沒有這個屬性, 則需要建立個屬性, 且值為空陣列
if (from[key] instanceof Array) if (!to[key]) to[key] = [];
extend(to[key], from[key]);
} else {
// 判斷源物件是否為陣列, 如果是直接push追加, 如果不是則新建屬性直接賦值
from instanceof Array ? to.push(from[key]) : to[key] = from[key];
}
}
return to;
}
複製程式碼
Demo:
let obj1 = {
name: '張三',
age: 12,
language: {
zh: '中文',
en: '英文'
},
color:['red','yellow']
};
let obj2 = {
name: '李四',
age: 22,
language: {
other: '其他'
},
color:['blank']
};
let options = extend(obj1, obj2);
console.log(options, JSON.stringify(options));
//'{"name":"李四","age":22,"language":{"zh":"中文","en":"英文","other":"其他"},"color":["red","yellow","blank"]}'
obj2.color.push('white');
console.log(options, JSON.stringify(options));
//'{"name":"李四","age":22,"language":{"zh":"中文","en":"英文","other":"其他"},"color":["red","yellow","blank"]}'
複製程式碼
如果obj1、obj2都是陣列合並又會怎麼樣呢?看下案例:
let obj1 = [{
name:'張三',
age:20,
language: {
zh: '中文',
en: '英文'
}
},{
name:'李四',
age:22,
language: {
zh: '中文',
en: '英文'
}
}];
let obj2 = [{
name:'王武',
age:23,
language: {
other: '其他'
},
color:['blank']
}];
let options = extend(obj1, obj2);
console.log(options, JSON.stringify(options));
//'[{"name":"王武","age":23,"language":{"zh":"中文","en":"英文","other":"其他"},"color":["blank"]},{"name":"李四","age":22,"language":{"zh":"中文","en":"英文"}}]'
obj2[0].name = '測試';
console.log(options, JSON.stringify(options));
//'[{"name":"王武","age":23,"language":{"zh":"中文","en":"英文","other":"其他"},"color":["blank"]},{"name":"李四","age":22,"language":{"zh":"中文","en":"英文"}}]'
複製程式碼
顯然陣列也是物件,for in
也是支援的,達到預期結果。
三、測試題
來一道測試題,看你是否瞭解,以下 console.log()
分別列印出什麼?
let num = 0;
function deepClone(obj) {
const newObj = obj instanceof Array ? [] : typeof obj === 'object' && typeof to !== 'function' ? {} : '';
if (!newObj) throw new Error('obj is not object');
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
num ++ ;
if (typeof obj[key] === 'object') {
newObj[key] = deepClone(obj[key]);
console.log(num,'中間');
} else {
newObj[key] = obj[key];
}
console.log(num,key);
}
}
return newObj;
}
let obj1 = {
name: '張三',
age: 12,
language: {
zh: '中文',
en: '英文'
},
color:['red','yellow']
};
const options = deepClone(obj1); // 執行該方法, 輸出結果看看?
複製程式碼
作者:hwgq2005