参考 https://www.fly63.com/article/detial/9650
文章目录
浅拷贝
只复制对象的第一层属性,如果对象的属性值是引用类型,则复制的是其引用地址。
初始化数据
let arr = [0, 1, [2, 3], 4];
let obj = { a: 1, b: { c: 2 }, d: () => 0 };
1. for 循环
function shallowClone(data) {
let res = Array.isArray(data) ? [] : {};
for (let k in data) res[k] = data[k];
return res;
}
let copy = shallowClone(arr);
console.log(copy); // [ 0, 1, [ 2, 3 ], 4 ]
copy[0] = 6;
copy[2][0] = 8;
console.log(arr); // [ 0, 1, [ 8, 3 ], 4 ]
console.log(copy); // [ 6, 1, [ 8, 3 ], 4 ]
2. Object.assign()
function shallowClone(data) {
return Object.assign({}, data);
}
let copy = shallowClone(obj);
console.log(copy, copy.d()); // { a: 1, b: { c: 2 }, d: [Function: d] } 0
copy.a = 6;
copy.d = () => 1;
copy.b.c = 8;
console.log(obj); // { a: 1, b: { c: 8 }, d: [Function: d] }
console.log(copy, copy.d()); // { a: 6, b: { c: 8 }, d: [Function (anonymous)] } 1
3. Object.create()
function shallowClone(data) {
let res = Object.create(Object.getPrototypeOf(data));
let names = Object.getOwnPropertyNames(data); // [ 'a', 'b', 'd' ]
for (let key of names) {
let val = Object.getOwnPropertyDescriptor(obj, key);
Object.defineProperty(res, key, val);
}
return res;
}
4. 扩展运算符 …
function shallowClone(data) {
return [...arr];
}
5. slice()
function shallowClone(data) {
return data.slice();
}
6. concat()
function shallowClone(data) {
return [].concat(data);
}
7. Array.from()
function shallowClone(data) {
return Array.from(data);
}
深拷贝
复制对象及其所有子对象,确保新旧对象及其内部的所有引用类型都不共享任何数据。
1. JSON.parse(JSON.stringify())
局限性:
- 只能处理Number,String,Boolean,Array,扁平对象。无法处理undefined、函数、循环引用等。
- 会丢失原对象的 constructor,无论原对象的构造函数如何定义,拷贝后的 constructor 都为 Object。
function deepClone(data) {
return JSON.parse(JSON.stringify(data));
}
let copy = deepClone(arr);
console.log(copy); // [ 0, 1, [ 2, 3 ], 4 ]
copy[0] = 6;
copy[2][0] = 8;
console.log(arr); // [ 0, 1, [ 2, 3 ], 4 ]
console.log(copy); // [ 6, 1, [ 8, 3 ], 4 ]
copy = deepClone(obj);
console.log(copy); // { a: 1, b: { c: 2 } }
copy.a = 6;
copy.d = () => 1;
copy.b.c = 8;
console.log(obj); // { a: 1, b: { c: 8 }, d: [Function: d] }
console.log(copy, copy.d()); // { a: 6, b: { c: 8 }, d: [Function (anonymous)] } 1
2. 递归拷贝(较完美的深拷贝)
/**
* 判断是否是基本数据类型
* @param value
*/
function isPrimitive(value) {
return (
typeof value === "string" ||
typeof value === "number" ||
typeof value === "symbol" ||
typeof value === "boolean"
);
}
/**
* 判断是否是一个js对象
* @param value
*/
function isObject(value) {
return Object.prototype.toString.call(value) === "[object Object]";
}
/**
* 深拷贝一个值
* @param value
*/
function deepClone(value) {
// 记录被拷贝的值,避免循环引用的出现
let memo = {};
function baseClone(value) {
let res;
// 如果是基本数据类型,则直接返回
if (isPrimitive(value)) return value;
// 如果是引用数据类型,我们浅拷贝一个新值来代替原来的值
else if (Array.isArray(value)) res = [...value];
else if (isObject(value)) res = { ...value };
// 检测我们浅拷贝的这个对象的属性值有没有是引用数据类型。如果是,则递归拷贝
Reflect.ownKeys(res).forEach((key) => {
if (typeof res[key] === "object" && res[key] !== null) {
//此处我们用memo来记录已经被拷贝过的引用地址。以此来解决循环引用的问题
if (memo[res[key]]) res[key] = memo[res[key]];
else {
memo[res[key]] = res[key];
res[key] = baseClone(res[key]);
}
}
});
return res;
}
return baseClone(value);
}
let copy = deepClone(arr);
console.log(copy); // [ 0, 1, [ 2, 3 ], 4 ]
copy[0] = 6;
copy[2][0] = 8;
console.log(arr); // [ 0, 1, [ 2, 3 ], 4 ]
console.log(copy); // [ 6, 1, [ 8, 3 ], 4 ]
copy = deepClone(obj);
console.log(copy); // { a: 1, b: { c: 8 }, d: [Function: d] }
copy.a = 6;
copy.d = () => 1;
copy.b.c = 8;
console.log(obj); // { a: 1, b: { c: 8 }, d: [Function: d] }
console.log(copy, copy.d()); // { a: 6, b: { c: 8 }, d: [Function (anonymous)] } 1
3. lodash 插件 cloneDeep()
const _ = require('lodash');
let copy= _.cloneDeep(obj);