以下是测试性能的环境
CPU | AMD Ryzen 9 5900HX with Radeon Graphics 八核 |
操作系统 | Windows 11 家庭中文版 (64位) |
内存 | 16GB(3200 MHz / 3200 MHz) |
Nodejs | v18.16.0 |
测试一:元素全是基础类型的数组
测试代码
import {cloneDeep, clone} from "lodash-es";
const time = 1000 * 10000;
const val = 'abcdefghijklmnopqrstuvwxyz';
const arr = new Array(time).fill(val);
let copyArr = [];
console.time('【深拷贝】JSON.parse');
copyArr = JSON.parse(JSON.stringify(arr));
console.timeEnd('【深拷贝】JSON.parse');
copyArr = [];
console.time('【深拷贝】lodash-es cloneDeep');
copyArr = cloneDeep(arr);
console.timeEnd('【深拷贝】lodash-es cloneDeep');
copyArr = [];
// 这个应该算浅拷贝,不过因为是基础类型,就划到深拷贝里了
console.time('【深拷贝】手动 for');
const length = arr.length;
copyArr = new Array(length);
for (let i = 0; i < length; i++) {
copyArr[i] = arr[i];
}
console.timeEnd('【深拷贝】手动 for');
// 自己实现一个深拷贝,不考虑一些特殊场景
function deepClone(source) {
if (typeof source !== 'object' || source == null) {
return source;
}
const target = Array.isArray(source) ? [] : {};
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = deepClone(source[key]);
} else {
target[key] = source[key];
}
}
}
return target;
}
copyArr = [];
console.time('【深拷贝】自己实现的deepClone');
copyArr = deepClone(arr);
console.timeEnd('【深拷贝】自己实现的deepClone');
copyArr = [];
console.time('【浅拷贝】lodash-es clone');
copyArr = clone(arr);
console.timeEnd('【浅拷贝】lodash-es clone');
copyArr = [];
console.time('【浅拷贝】concat');
copyArr = arr.concat();
console.timeEnd('【浅拷贝】concat');
copyArr = [];
console.time('【浅拷贝】slice');
copyArr = arr.slice();
console.timeEnd('【浅拷贝】slice');
copyArr = [];
console.time('【浅拷贝】Object.assign()');
copyArr = Object.assign([], arr);
console.timeEnd('【浅拷贝】Object.assign()');
copyArr = [];
console.time('【浅拷贝】手动 扩展运算符');
copyArr = [...arr];
console.timeEnd('【浅拷贝】手动 扩展运算符');
测试结果
深拷贝 | 浅拷贝 | |||||||||||||
JSON | lodash cloneDeep | for | 自己实现的深拷贝 | loadash clone | concat | slice | Object.assign | 扩展运算符 | ||||||
数字999999.99999 | 1000万 | 4.020s | 479.56ms | 94.122ms | 1.93s | 185.31ms | 37.38ms | 35.06ms | 8.198s | 73.90ms | ||||
10万 | 22.823ms | 6.619ms | 2.079ms | 12.044ms | 1.907ms | 0.334ms | 0.372ms | 24.024ms | 0.411ms | |||||
1000 | 0.272ms | 0.638ms | 0.162ms | 0.301ms | 0.07ms | 0.008ms | 0.005ms | 0.241ms | 0.004ms | |||||
数字1 | 1000万 | 613.693ms | 336.968ms | 57.705ms | 1.978s | 92.962ms | 72.879ms | 60.213ms | 5.626s | 56.124ms | ||||
10万 | 5.207ms | 5.819ms | 1.663ms | 11.275ms | 1.661ms | 0.404ms | 0.32ms | 23.207ms | 0.305ms | |||||
1000 | 0.118ms | 0.606ms | 0.16ms | 0.338ms | 0.071ms | 0.008ms | 0.004ms | 0.227ms | 0.006ms | |||||
字符串 abcdefghijklmnopqrstuvwxyz | 1000万 | 3.938s | 355.628ms | 96.017ms | 2.092s | 129.239ms | 128.126ms | 52.62ms | 5.353s | 84.042ms | ||||
10万 | 12.666ms | 5.993ms | 1.729ms | 10.609ms | 1.845ms | 0.306ms | 0.319ms | 23.48ms | 0.373ms | |||||
1000 | 0.169ms | 0.625ms | 0.174ms | 0.424ms | 0.122ms | 0.011ms | 0.011ms | 0.164ms | 0.005ms |
结果分析
从测试结果上来看,浅拷贝的效率要比深拷贝高很多,所以如果数组中都是基础数据类型的化建议直接使用浅拷贝。
如果数组中都是基础数据类型,那么无脑浅拷贝就可以了。
数组深拷贝的时候在数据量大的时候loadash的cloneDeep性能比较好,如果数据量小的时候JSON API比较快。
JSON API的速度和数组转字符串(JSON.stringify)的长度有明显的关系。
浅拷贝中Object.assign太拉垮了。
测试二:属性全是基础类型的对象
测试代码
import {cloneDeep, clone} from "lodash-es";
const time = 1000 * 1000;
const obj = {};
const per = time / 5;
for (let i = 0; i < per; i++) {
obj['test1111' + i] = i;
}
for (let i = 0; i < per; i++) {
obj['APPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP' + i] = i.toString();
}
for (let i = 0; i < per; i++) {
obj['XXXX' + i] = true;
}
for (let i = 0; i < per; i++) {
obj['str11334566444' + i] = 1.111222333;
}
for (let i = 0; i < per; i++) {
obj['str' + i] = '测试一个字符串';
}
let copy;
console.time('【深拷贝】JSON.parse');
copy = JSON.parse(JSON.stringify(obj));
console.timeEnd('【深拷贝】JSON.parse');
console.time('【深拷贝】lodash-es cloneDeep');
copy = cloneDeep(obj);
console.timeEnd('【深拷贝】lodash-es cloneDeep');
// 这个应该算浅拷贝,不过因为是基础类型,就划到深拷贝里了
console.time('【深拷贝】手动 for');
Object.keys(obj).forEach((key) => {
copy[key] = obj[key];
})
console.timeEnd('【深拷贝】手动 for');
// 自己实现一个深拷贝,不考虑一些特殊场景
function deepClone(source) {
if (typeof source !== 'object' || source == null) {
return source;
}
const target = Array.isArray(source) ? [] : {};
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = deepClone(source[key]);
} else {
target[key] = source[key];
}
}
}
return target;
}
console.time('【深拷贝】自己实现的deepClone');
copy = deepClone(obj);
console.timeEnd('【深拷贝】自己实现的deepClone');
console.time('【浅拷贝】lodash-es clone');
copy = clone(obj);
console.timeEnd('【浅拷贝】lodash-es clone');
console.time('【浅拷贝】Object.assign()');
copy = Object.assign({}, obj);
console.timeEnd('【浅拷贝】Object.assign()');
console.time('【浅拷贝】手动 扩展运算符');
copy = {...obj};
console.timeEnd('【浅拷贝】手动 扩展运算符');
测试结果
深拷贝 | 浅拷贝 | ||||||
JSON | lodash cloneDeep | for | 自己实现的深拷贝 | loadash clone | Object.assign | 扩展运算符 | |
100万 | 1.620s | 605.588ms | 410.262ms | 607.865ms | 638.868ms | 930.889ms | 924.655ms |
10万 | 76.511ms | 43.793ms | 25.555ms | 49.19ms | 41.705ms | 57.76ms | 76.651ms |
1000 | 0.468ms | 0.78ms | 0.172ms | 0.292ms | 0.318ms | 0.291ms | 2.736ms |
结果分析
造1000W个属性的数据卡死了,估计实际场景也不会有这种对象,所以放弃了。
没想到浅拷贝的性能也不太理想,最好的居然是循环赋值。(如果数据都是基础数据类型,可以考虑自己循环实现拷贝)
测试三:属性中带有50%非基础类型的对象
测试代码
import {cloneDeep, clone} from "lodash-es";
const time = 1000 * 10;
const obj = {};
const per = time / 4;
for (let i = 0; i < per; i++) {
obj['test1111' + i] = {
name: 'name',
sex: '男',
age: 18,
number: 22222222222,
isDel: false,
date: '2021-02-02',
};
}
for (let i = 0; i < per; i++) {
obj['APPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP' + i] = [1, 4, 'aaa', true, false, 99.22331, {
name: 'name',
sex: '男',
age: 18,
number: 22222222222,
isDel: false,
date: '2021-02-02',
}];
}
for (let i = 0; i < per; i++) {
obj['XXXX' + i] = true;
}
for (let i = 0; i < per; i++) {
obj['str11334566444' + i] = i & 0 ? 10 : 1.111222333;
}
let copy;
console.time('【深拷贝】JSON.parse');
copy = JSON.parse(JSON.stringify(obj));
console.timeEnd('【深拷贝】JSON.parse');
console.time('【深拷贝】lodash-es cloneDeep');
copy = cloneDeep(obj);
console.timeEnd('【深拷贝】lodash-es cloneDeep');
// 自己实现一个深拷贝,不考虑一些特殊场景
function deepClone(source) {
if (typeof source !== 'object' || source == null) {
return source;
}
const target = Array.isArray(source) ? [] : {};
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = deepClone(source[key]);
} else {
target[key] = source[key];
}
}
}
return target;
}
console.time('【深拷贝】自己实现的deepClone');
copy = deepClone(obj);
console.timeEnd('【深拷贝】自己实现的deepClone');
console.time('【浅拷贝】lodash-es clone');
copy = clone(obj);
console.timeEnd('【浅拷贝】lodash-es clone');
console.time('【浅拷贝】Object.assign()');
copy = Object.assign({}, obj);
console.timeEnd('【浅拷贝】Object.assign()');
console.time('【浅拷贝】手动 扩展运算符');
copy = {...obj};
console.timeEnd('【浅拷贝】手动 扩展运算符');
测试结果
深拷贝 | 浅拷贝 | ||||||
JSON | lodash cloneDeep | 自己实现的深拷贝 | for | loadash clone | Object.assign | 扩展运算符 | |
100万 | 2.142s | 1.716s | 1.074s | 365.295ms | 669.646ms | 1.017s | 991.849ms |
10万 | 166.046ms | 159.29ms | 80.986ms | 23.6ms | 42.761ms | 62.753ms | 62.581ms |
1000 | 1.103ms | 3.927ms | 0.947ms | 0.17ms | 0.359ms | 0.351ms | 2.853ms |
结果分析
有50%的属性为引用类型的情况下,浅拷贝的整体性能要优于深拷贝。其中自己实现循环仍然遥遥领先。(如果只要浅拷贝就可以的话可以试一试)