一、浅拷贝
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