JavaScript API性能对比系列二:深拷贝和浅拷贝性能对比(二)

内容接上一篇

JavaScript API性能对比系列二:深拷贝和浅拷贝性能对比-CSDN博客

以下是测试性能的环境

CPUAMD Ryzen 9 5900HX with Radeon Graphics 八核
操作系统Windows 11 家庭中文版 (64位)
内存16GB(3200 MHz / 3200 MHz)
Nodejsv18.16.0

测试四:元素全是对象类型的数组

测试代码

import {cloneDeep, clone} from "lodash-es";

const time = 1000000;
const date = new Date();
const arr = new Array(time).fill(null);
for (let i = 0; i < arr.length; i++) {
    arr[i] = {
        key1: 'string',
        key2: 'string2',
        key3: 'string3',
        key4: 'string4',
        key5: 'string5',
        key6: 'string6',
        key7: 'string7',
        key8: 'string8',
        key9: 'string9',
        key10: 123456789,
        key11: 123456789.12345,
        key12: 12345,
        key13: 12345.12,
        key14: null,
        key15: null,
        key16: undefined,
        key17: undefined,
        key18: {
            key1: '12345,67899,234567',
            key2: true,
            key3: true,
            key4: false,
            key5: null,
            key6: null,
            key7: null,
            key8: null,
            key9: 12345,
            key10: 65432,
        },
        key19: [1, 2, 3, 4, 5, 111, 222, 444.555, 222.11111, 333, 2222],
        key20: date
    };
}

let copy;
console.time('【深拷贝】JSON.parse');
copy = JSON.parse(JSON.stringify(arr));
console.timeEnd('【深拷贝】JSON.parse');

console.time('【深拷贝】lodash-es cloneDeep');
copy = cloneDeep(arr);
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(arr);
console.timeEnd('【深拷贝】自己实现的deepClone');

console.time('【深拷贝】手动for + 手动赋值');
const length1 = arr.length;
copy = new Array(length1);
for (let i = 0; i < length1; i++) {
    const item = arr[i];
    const itemKey18 = item.key18;
    const itemKey19 = item.key19;
    copy[i] = {
        key1: item.key1,
        key2: item.key2,
        key3: item.key3,
        key4: item.key4,
        key5: item.key5,
        key6: item.key6,
        key7: item.key7,
        key8: item.key8,
        key9: item.key9,
        key10: item.key10,
        key11: item.key11,
        key12: item.key12,
        key13: item.key13,
        key14: item.key14,
        key15: item.key15,
        key16: item.key16,
        key17: item.key17,
        key18: {
            key1: itemKey18.key1,
            key2: itemKey18.key2,
            key3: itemKey18.key3,
            key4: itemKey18.key4,
            key5: itemKey18.key5,
            key6: itemKey18.key6,
            key7: itemKey18.key7,
            key8: itemKey18.key8,
            key9: itemKey18.key9,
            key10: itemKey18.key10,
        },
        key19: [...itemKey19],  // 模拟真实场景一般数组的长度不固定
        key20: item.key20
    };
}
console.timeEnd('【深拷贝】手动for + 手动赋值');

console.time('【浅拷贝】lodash-es clone');
copy = clone(arr);
console.timeEnd('【浅拷贝】lodash-es clone');


console.time('【浅拷贝】concat');
copy = arr.concat();
console.timeEnd('【浅拷贝】concat');


console.time('【浅拷贝】slice');
copy = arr.slice();
console.timeEnd('【浅拷贝】slice');


console.time('【浅拷贝】Object.assign()');
copy = Object.assign([], arr);
console.timeEnd('【浅拷贝】Object.assign()');


console.time('【浅拷贝】手动 扩展运算符');
copy = [...arr];
console.timeEnd('【浅拷贝】手动 扩展运算符');

console.time('【浅拷贝】手动 for');
const length = arr.length;
copy = new Array(length);
for (let i = 0; i < length; i++) {
    copy[i] = arr[i];
}
console.timeEnd('【浅拷贝】手动 for');

新加了一个测试场景,就是手动为每个属性赋值,结果大大出乎我的意料(代码请看【深拷贝】手动for + 手动赋值),下面是测试结果。

测试结果

深拷贝浅拷贝
JSONlodash cloneDeep自己实现的深拷贝for + 手动赋值loadash cloneconcatsliceObject.assign()扩展运算符for
100万8.216s8.294s5.850s1.178s16.881ms14.206ms3.547ms345.892ms4.042ms13.563ms
10万659.265ms733.335ms371.223ms35.063ms2.366ms0.347ms0.374ms34.441ms0.497ms2.047ms
10005.815ms13.723ms6.027ms0.494ms0.161ms0.012ms0.01ms0.223ms0.004ms0.034ms

结果分析

这次新加的【深拷贝】手动for + 手动赋值 这种深拷贝性能非常高,看来这种一次完整的对象赋值,比一个属性一个属性赋值效率要高很多,基本差了5~8倍。

浅拷贝中slice和扩展运算符性能比较好。

测试五:二维数组

测试代码

import {cloneDeep, clone} from "lodash-es";

const time = 1000000;
const date = new Date();
const arr = new Array(time).fill(null);
for (let i = 0; i < arr.length; i++) {
    // 共15个元素
    arr[i] = [111, 2222, 3333.333, 4444.44, null, undefined, null, undefined, date, '测试', '测试用例1234', true, true, false, true]
}

let copy;
console.time('【深拷贝】JSON.parse');
copy = JSON.parse(JSON.stringify(arr));
console.timeEnd('【深拷贝】JSON.parse');

console.time('【深拷贝】lodash-es cloneDeep');
copy = cloneDeep(arr);
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(arr);
console.timeEnd('【深拷贝】自己实现的deepClone');

console.time('【深拷贝】手动for + 手动赋值');
const length1 = arr.length;
copy = new Array(length1);
for (let i = 0; i < length1; i++) {
    const item = arr[i];
    copy[i] = [item[0], item[1], item[2], item[3], item[4], item[5], item[6], item[7], item[8], item[9], item[10], item[11], item[12], item[13], item[14]];
}
console.timeEnd('【深拷贝】手动for + 手动赋值');

console.time('【浅拷贝】lodash-es clone');
copy = clone(arr);
console.timeEnd('【浅拷贝】lodash-es clone');


console.time('【浅拷贝】concat');
copy = arr.concat();
console.timeEnd('【浅拷贝】concat');


console.time('【浅拷贝】slice');
copy = arr.slice();
console.timeEnd('【浅拷贝】slice');


console.time('【浅拷贝】Object.assign()');
copy = Object.assign([], arr);
console.timeEnd('【浅拷贝】Object.assign()');


console.time('【浅拷贝】手动 扩展运算符');
copy = [...arr];
console.timeEnd('【浅拷贝】手动 扩展运算符');

console.time('【浅拷贝】手动 for');
const length = arr.length;
copy = new Array(length);
for (let i = 0; i < length; i++) {
    copy[i] = arr[i];
}
console.timeEnd('【浅拷贝】手动 for');

新加了一个测试场景,就是手动为每个属性赋值,结果大大出乎我的意料(代码请看【深拷贝】手动for + 手动赋值),下面是测试结果。

测试结果

深拷贝浅拷贝
JSONlodash cloneDeep自己实现的深拷贝for + 手动赋值loadash cloneconcatsliceObject.assign()扩展运算符for
100万3.294s1.594s1.188s176.17ms8.455ms6.188ms9.102ms365.283ms4.909ms10.175ms
10万278.327ms162.958ms106.819ms17.46ms1.628ms0.34ms0.304ms23.34ms0.383ms2.643ms
10002.399ms5.132ms2.292ms0.336ms0.191ms0.01ms0.005ms0.188ms0.004ms0.037ms

结果分析

【深拷贝】手动for + 手动赋值 性能依旧炸裂。

浅拷贝扩展运算符性能很稳定。

Object.assign()在处理数组的时候真的不行。

最终总结

1. 如果能够选择浅拷贝的情况下请尽量选择浅拷贝(尤其是对象或者数组中都是基础类型,务必使用浅拷贝)。

2. 浅拷贝如果是对象请使用lodash的clone方法,数组的话请使用扩展运算符[...arr]。

3. 深拷贝中,我最后新加的测试场景【深拷贝】手动for + 手动赋值 的性能实在是太好了,所以如果能够手动赋值还是尽量手动赋值。

for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    copy.push({
        key1: item.key1,
        key2: item.key2,
        key3: item.key3,
        key4: item.key4,
        key5: item.key5,
    })
}

4. 深拷贝中 在几千条以下的时候JSON.parse(JSON.stringify(obj)) 性能要比loadash的cloneDeep性能好。数据量大的情况下反之。

5. 如果对象并不复杂,可以选择自己手写深拷贝方法(loadash的深拷贝为了兼容很多场景做了很多判断)。

6. 如果对象(数组)转字符串越长JSON.parse(JSON.stringify(obj)) 性能越差。

  • 40
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值