目录
一、浅拷贝的实现
1. Object.assgin
const obj = {
name: 'star',
age: 18,
friend: {
name: 'coder'
}
};
// 浅拷贝
const info = Object.assign({}, obj);
console.log(info); // { name: 'star', age: 18, friend: { name: 'coder' } }
// 修改引用类型数据,同时会更改
obj.friend.name = '123';
console.log(info); // { name: 'star', age: 18, friend: { name: '123' } }
2. 展开运算符
const obj = {
name: 'star',
age: 18,
friend: {
name: 'coder'
}
};
// 浅拷贝
const info = { ...obj };
console.log(info); // { name: 'star', age: 18, friend: { name: 'coder' } }
// 修改引用类型数据,同时会更改
obj.friend.name = '123';
console.log(info); // { name: 'star', age: 18, friend: { name: '123' } }
3. Array.prototype.concat
const arr = [1, { name: 'coder' }, '4'];
// 浅拷贝
const info = [].concat.call(arr, [{ age: 18 }]);
// const info = Array.prototype.concat.call(arr, [{ age: 18 }]);
console.log(info); //[ 1, { name: 'coder' }, '4', { age: 18 } ]
// 修改引用类型数据,同时会更改
arr[1].name = '123';
console.log(info); // [ 1, { name: '123' }, '4', { age: 18 } ]
4. Array.prototype.slice
const arr = [1, 2, { name: 'coder' }, '4'];
// 浅拷贝
const info = [].slice.call(arr);
// const info = [].slice.call(arr, 0);
// const info = [].slice.call(arr, 0, 4);
// const info = Array.prototype.slice.call(arr);
console.log(info); // [ 1, 2, { name: 'coder' }, '4' ]
// 修改引用类型数据,同时会更改
arr[2].name = '123';
console.log(info); // [ 1, 2, { name: '123' }, '4' ]
二、深拷贝的实现
0. 判断一个值是否是对象类型
// 判断一个标识符是否是对象类型
function isObject(value) {
const valueType = typeof value;
return value !== null && (valueType === 'object' || valueType === 'function');
}
1. JSON实现
const obj = {
name: 'star',
age: 18,
friend: {
name: 'coder',
arr: [1, 2, 3, 4]
}
};
// 深拷贝
const info = JSON.parse(JSON.stringify(obj));
console.log(info);
// 修改原来属性,不会影响新创建的对象
obj.friend.name = '123';
console.log(info);
缺点 :
1. function 不会被转化,直接忽略掉了
2. symbal 不会被转化,直接忽略掉了
3. 存在undefined、NaN 之类情况会转化错误
4. 存在循环引用的情况直接报错
2. 手写深拷贝函数
01 - 基本手写
function isObject(value) {
const valueType = typeof value;
return value !== null && (valueType === 'object' || valueType === 'function');
}
function deepCopy(originValue) {
// 1. 如果不是引用类型,直接返回
if (!isObject(originValue)) {
return originValue;
}
// 2. 创建新对象
const newObj = {};
for (const key in originValue) {
// 3. 赋值的时候,进行递归
newObj[key] = deepCopy(originValue[key]);
}
// 4. 返回新对象
return newObj;
}
const obj = {
name: 'star',
age: 18,
friend: {
name: 'coder'
}
};
// 深拷贝
const info = deepCopy(obj);
console.log(info);
// 修改原来属性,不会影响新创建的对象
obj.friend.name = '123';
console.log(info);
02 - 优化 => 数组
function isObject(value) {
const valueType = typeof value;
return value !== null && (valueType === 'object' || valueType === 'function');
}
function deepCopy(originValue) {
// 1. 如果不是引用类型,直接返回
if (!isObject(originValue)) {
return originValue;
}
// 2. 创建新对象 | 数组
const newObj = Array.isArray(originValue) ? [] : {};
for (const key in originValue) {
// 3. 赋值的时候,进行递归
newObj[key] = deepCopy(originValue[key]);
}
// 4. 返回新对象
return newObj;
}
const obj = {
name: 'star',
age: 18,
friend: {
name: 'coder',
info: [1, 2, 3, 4, 5]
}
};
// 深拷贝
const info = deepCopy(obj);
console.log(info);
obj.age = 20;
console.log(info);
03 - 优化 => 函数、Set、Map、Symbol
function isObject(value) {
const valueType = typeof value;
return value !== null && (valueType === 'object' || valueType === 'function');
}
// 深拷贝函数
function deepCopy(originValue) {
/**
* 值是 symbol数据类型
* 需写在基本数据类型之前,否则会被返回错误
*/
if (typeof originValue === 'symbol') {
return Symbol(originValue.description);
}
/**
* 基本数据类型,直接返回
*/
if (!isObject(originValue)) {
return originValue;
}
/**
* 函数类型
* 不需要拷贝,直接返回
*/
if (typeof originValue === 'function') {
return originValue;
}
/**
* set类型
* 因为set类型不能被for...in,所以单独处理
*/
if (originValue instanceof Set) {
const newSet = new Set();
for (const item of originValue) {
newSet.add(deepCopy(item));
}
return newSet;
}
/**
* map类型
* 因为set类型不能被for...in,所以单独处理
*/
if (originValue instanceof Map) {
const newMap = new Map();
for (const [key, value] of originValue) {
newMap.set(deepCopy(key), deepCopy(value));
}
return newMap;
}
/**
* 对象,数组类型
*/
const newObj = Array.isArray(originValue) ? [] : {};
// one - 遍历普通的key
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
// two - 遍历Symbol的key
const symbolKeys = Object.getOwnPropertySymbols(originValue);
for (const symbolKey of symbolKeys) {
newObj[Symbol(symbolKey.description)] = deepCopy(originValue[symbolKey]);
}
// 4. 返回新对象 | 数组
return newObj;
}
const symbolKey = Symbol('key是Symbol');
const obj = {
name: 'star',
age: 18,
friend: {
name: 'coder',
info: [1, 2, 3, 4, 5]
},
play() {
console.log('playing!');
},
set: new Set([1, 22, 3]),
map: new Map([
[{ name: 5 }, 5],
[6, { name: 6 }]
]),
[symbolKey]: 'key是Symbol',
symbolValue: Symbol('value是Symbol')
};
// 深拷贝
const info = deepCopy(obj);
console.log(info);
04 - 优化 => 循环引用
function isObject(value) {
const valueType = typeof value;
return value !== null && (valueType === 'object' || valueType === 'function');
}
/**
* 循环引用解决方案 :
* 拷贝过的对象,不需要再次拷贝,直接返回即可
* 1. 使用 WeakMap 进行弱引用,用来判断是否拷贝过
* 2. 使用同一个 WeakMap
*/
function deepCopy(originValue, map = new WeakMap()) {
if (typeof originValue === 'symbol') {
return Symbol(originValue.description);
}
if (!isObject(originValue)) {
return originValue;
}
if (typeof originValue === 'function') {
return originValue;
}
if (originValue instanceof Set) {
const newSet = new Set();
for (const item of originValue) {
// 为了使得使用的 map 都为同一个,调用时把map传入,使得map 都为最开始创建的那一个
newSet.add(deepCopy(item, map));
}
return newSet;
}
if (originValue instanceof Map) {
const newMap = new Map();
for (const [key, value] of originValue) {
// 为了使得使用的 map 都为同一个,调用时把map传入,使得map 都为最开始创建的那一个
newMap.set(deepCopy(key, map), deepCopy(value, map));
}
return newMap;
}
/**
* 每次创建对象前,判断map中是否已经拥有了 => 即是否已经拷贝过了
* 如果有,直接返回以前创建的对象
*/
if (map.get(originValue)) {
return map.get(originValue);
}
const newObj = Array.isArray(originValue) ? [] : {};
/**
* 1. 每次创建新对象后,推入 WeakMap 中,用来保存
* 2. 当前的对象的地址作为 key ,值为新创建出来的对象
*/
map.set(originValue, newObj);
for (const key in originValue) {
// 为了使得使用的 map 都为同一个,调用时把map传入,使得map 都为最开始创建的那一个
newObj[key] = deepCopy(originValue[key], map);
}
const symbolKeys = Object.getOwnPropertySymbols(originValue);
for (const symbolKey of symbolKeys) {
// 为了使得使用的 map 都为同一个,调用时把map传入,使得map 都为最开始创建的那一个
newObj[Symbol(symbolKey.description)] = deepCopy(originValue[symbolKey], map);
}
return newObj;
}
const symbolKey = Symbol('key是Symbol');
const obj = {
name: 'star',
age: 18,
friend: {
name: 'coder',
info: [1, 2, 3, 4, 5]
}
};
// 自己引用自己
obj.obj = obj;
// 深拷贝
const info = deepCopy(obj);
console.log(info);
04 - 最终方案
// 判断一个值是否是对象类型
function isObject(value) {
const valueType = typeof value;
return value !== null && (valueType === 'object' || valueType === 'function');
}
// 深拷贝函数
function deepCopy(originValue, map = new WeakMap()) {
if (typeof originValue === 'symbol') {
return Symbol(originValue.description);
}
if (!isObject(originValue)) {
return originValue;
}
if (typeof originValue === 'function') {
return originValue;
}
if (originValue instanceof Set) {
const newSet = new Set();
for (const item of originValue) {
newSet.add(deepCopy(item, map));
}
return newSet;
}
if (originValue instanceof Map) {
const newMap = new Map();
for (const [key, value] of originValue) {
newMap.set(deepCopy(key, map), deepCopy(value, map));
}
return newMap;
}
if (map.get(originValue)) {
return map.get(originValue);
}
const newObj = Array.isArray(originValue) ? [] : {};
map.set(originValue, newObj);
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key], map);
}
const symbolKeys = Object.getOwnPropertySymbols(originValue);
for (const symbolKey of symbolKeys) {
newObj[Symbol(symbolKey.description)] = deepCopy(originValue[symbolKey], map);
}
return newObj;
}