JavaScript 对象合并方法详解及最佳实践

 更新时间:2026年02月08日 08:45:21   作者:DEMO派  
在JavaScript开发中,对象和数组的操作非常频繁,这篇文章主要介绍了JavaScript对象合并方法详解及最佳实践的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、常用对象合并方法

1.1 展开运算符 (Spread Operator)

const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const result = { ...obj1, ...obj2 };
// { a: 1, b: 3, c: 4 }

优点

  • 语法简洁直观

  • ES6+ 原生支持

  • 创建新对象,不影响原对象

缺点

  • 浅拷贝(嵌套对象只复制引用)

  • 无法合并复杂结构(如数组、特殊对象)

  • 不支持深度合并

1.2 Object.assign()

const obj1 = { a: 1, b: { x: 1 } };
const obj2 = { b: { y: 2 }, c: 3 };
const result = Object.assign({}, obj1, obj2);
// { a: 1, b: { y: 2 }, c: 3 } // 注意:b.x 被覆盖

优点

  • ES5+ 兼容性好

  • 可合并多个对象

  • 创建新对象(当第一个参数为{}时)

缺点

  • 浅拷贝

  • 会覆盖同名属性

  • 无法处理getter/setter

1.3 手动深度合并函数

function deepMerge(target, ...sources) {
    if (!sources.length) return target;
    const source = sources.shift();
    
    if (isObject(target) && isObject(source)) {
        for (const key in source) {
            if (isObject(source[key])) {
                if (!target[key]) Object.assign(target, { [key]: {} });
                deepMerge(target[key], source[key]);
            } else {
                Object.assign(target, { [key]: source[key] });
            }
        }
    }
    return deepMerge(target, ...sources);
}

function isObject(item) {
    return item && typeof item === 'object' && !Array.isArray(item);
}

const obj1 = { a: 1, b: { x: 1, y: 2 } };
const obj2 = { b: { y: 3, z: 4 }, c: 5 };
const result = deepMerge({}, obj1, obj2);
// { a: 1, b: { x: 1, y: 3, z: 4 }, c: 5 }

优点

  • 可深度合并

  • 高度可定制

  • 控制合并策略

缺点

  • 需要自己实现和维护

  • 可能性能较低

  • 需考虑循环引用

1.4 structuredClone() 方法

const obj1 = { 
    a: 1, 
    b: { 
        x: 1,
        nested: { deep: 'value' }
    },
    c: [1, 2, 3],
    date: new Date(),
    map: new Map([['key', 'value']]),
    set: new Set([1, 2, 3])
};

// 深度克隆
const cloned = structuredClone(obj1);

// 合并使用示例
const obj2 = { b: { y: 2 }, d: 4 };
const result = { ...structuredClone(obj1), ...obj2 };
// 或手动合并克隆后的对象
const merged = Object.assign(structuredClone(obj1), obj2);

优点

  • 真正的深度克隆(包括嵌套对象)

  • 支持复杂类型:Date、Map、Set、ArrayBuffer等

  • 官方标准,现代浏览器原生支持

  • 处理循环引用

  • 不共享任何引用

缺点

  • 不能克隆函数、DOM节点、Error对象等

  • 性能开销较大(特别是大对象)

  • Node.js 17+ 和现代浏览器才支持

  • 不能自定义合并策略

  • 只能克隆,需要配合其他方法实现合并

1.5 Lodash 的 merge/mergeWith

// 需要安装 lodash
import { merge, mergeWith } from 'lodash';

const obj1 = { a: 1, b: { x: 1 } };
const obj2 = { b: { y: 2 }, c: 3 };
const result = merge({}, obj1, obj2);
// { a: 1, b: { x: 1, y: 2 }, c: 3 }

// 自定义合并逻辑
const customizer = (objValue, srcValue) => {
    if (Array.isArray(objValue)) {
        return objValue.concat(srcValue);
    }
};
const result2 = mergeWith({}, obj1, obj2, customizer);

优点

  • 功能强大,深度合并

  • 支持自定义合并逻辑

  • 处理各种边缘情况

  • 社区维护,稳定可靠

缺点

  • 增加包体积

  • 需要引入外部库

1.6 JSON 方法(不推荐用于合并)

const obj1 = { a: 1, b: { x: 1 } };
const obj2 = { b: { y: 2 } };
const result = JSON.parse(JSON.stringify(obj1));
Object.assign(result, obj2);
// 注意:这种方式有局限性,不处理函数、循环引用等

优点

  • 简单暴力

  • 创建完全独立的拷贝

缺点

  • 丢失函数、undefined、Symbol等

  • 无法处理循环引用

  • 性能较差

二、其他特殊场景方法

2.1 深度合并库:deepmerge

import deepmerge from 'deepmerge';

const obj1 = { a: 1, b: { x: 1 }, arr: [1, 2] };
const obj2 = { b: { y: 2 }, arr: [3, 4] };

// 默认合并
const result1 = deepmerge(obj1, obj2);
// { a: 1, b: { x: 1, y: 2 }, arr: [3, 4] }

// 数组合并策略
const result2 = deepmerge(obj1, obj2, {
    arrayMerge: (target, source) => target.concat(source)
});
// { a: 1, b: { x: 1, y: 2 }, arr: [1, 2, 3, 4] }

2.2 使用 Proxy 实现响应式合并

function reactiveMerge(target, source) {
    return new Proxy({...target, ...source}, {
        set(obj, prop, value) {
            console.log(`Property ${prop} changed to ${value}`);
            obj[prop] = value;
            return true;
        }
    });
}

三、方法对比

四、总结与建议

根据数据结构和需求选择

  • 简单对象浅合并:扩展运算符 {…a, …b}

  • 需要兼容旧环境:Object.assign({}, a, b)

  • 包含特殊类型(Date、Map等)的深度克隆:structuredClone()

  • 复杂深度合并且已使用工具库:_.merge()

  • 完全控制合并逻辑:自定义递归函数

structuredClone适用场景

// 场景1:需要深度克隆包含Date、Map等特殊类型
const config = {
  lastUpdated: new Date(),
  permissions: new Set(['read', 'write']),
  metadata: new Map()
};
const backup = structuredClone(config);

// 场景2:处理可能包含循环引用的对象
const objA = { name: 'A' };
const objB = { name: 'B', ref: objA };
objA.ref = objB; // 循环引用
const cloned = structuredClone(objA); // 正确处理

// 场景3:需要转移数据到Worker线程
const largeData = { /* 大数据对象 */ };
const worker = new Worker('worker.js');
worker.postMessage(structuredClone(largeData));

现代项目最佳实践

// 1. 创建工具函数,根据情况选择最佳方案
export function smartClone(obj, deep = false) {
  if (!deep) {
    return Array.isArray(obj) ? [...obj] : { ...obj };
  }
  
  // 检查是否包含不支持的类型
  const hasFunctions = JSON.stringify(obj, (key, value) => {
    return typeof value === 'function' ? undefined : value;
  }) === undefined;
  
  if (hasFunctions || typeof structuredClone === 'undefined') {
    // 回退到自定义深度克隆或JSON方法
    return deepCloneFallback(obj);
  }
  
  return structuredClone(obj);
}

// 2. 对象合并的通用函数
export function mergeObjects(target, source, options = {}) {
  const { deep = false, cloneStrategy = 'auto' } = options;
  
  if (!deep) {
    return { ...target, ...source };
  }
  
  if (cloneStrategy === 'structuredClone') {
    return deepMergeWithClone(target, source);
  } else if (cloneStrategy === 'json') {
    // 仅用于可序列化数据
    const cloned = JSON.parse(JSON.stringify(target));
    return deepMergeRecursive(cloned, source);
  } else {
    // 默认使用lodash或自定义深度合并
    return deepMergeCustom(target, source, options);
  }
}

浏览器兼容性处理

// 特征检测与polyfill
function safeStructuredClone(obj) {
  if (typeof structuredClone === 'function') {
    try {
      return structuredClone(obj);
    } catch (error) {
      // 处理不支持的类型
      console.warn('structuredClone failed:', error);
      return fallbackClone(obj);
    }
  } else {
    // 降级方案
    return fallbackClone(obj);
  }
}

function fallbackClone(obj) {
  // 实现一个支持基本类型的深度克隆
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  if (obj instanceof Date) {
    return new Date(obj);
  }
  
  if (obj instanceof Array) {
    return obj.map(item => fallbackClone(item));
  }
  
  if (obj instanceof Object) {
    const cloned = {};
    for (const key in obj) {
      if (Object.hasOwn(obj, key)) {
        cloned[key] = fallbackClone(obj[key]);
      }
    }
    return cloned;
  }
  
  // 其他类型直接返回(包括函数)
  return obj;
}

最终建议总结

  • 默认选择扩展运算符:用于大多数浅合并场景,语法简洁性能好

  • 深度合并考虑需求:优先使用structuredClone(现代浏览器),备选lodash或自定义函数

  • 注意数据类型:有函数时不能用structuredClone和JSON方法

  • 考虑性能:大数据量或频繁操作时避免深度克隆

  • 做好兼容性:生产环境使用特征检测和降级方案

  • 明确合并策略:特别是数组和特殊对象的处理逻辑

适用场景

浅合并优先使用原生方法,深度操作评估是否需要引入库,根据实际数据结构和性能需求选择,保持团队代码风格统一。

总结

到此这篇关于JavaScript对象合并方法详解及最佳实践的文章就介绍到这了,更多相关JS对象合并方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论