You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何最高效对比两个JS对象的属性值并生成对比结果对象

高性能对象属性对比实现方案

原有实现的问题

你给出的初始实现不仅性能不足,还存在核心逻辑错误,无法满足需求:

  • 只遍历了第一个入参的属性,完全遗漏第二个对象独有的属性
  • 代码中引用了未定义的object变量,运行直接报错
  • 没有校验第二个对象的对应属性是否存在,当某侧缺少嵌套对象时,递归传入undefined会触发异常
  • 用constructor.name判断对象类型可靠性差,且会把数组误判为普通对象进入递归,不符合需求中数组直接复制原值的要求
  • 用Object.keys + forEach遍历会先生成键名数组,产生不必要的内存开销,遍历速度偏慢

最优实现(兼顾性能与正确性)

实现核心思路:

  • 时间复杂度控制在O(N),N为两个对象所有自有属性总数,每个属性仅访问一次,无冗余遍历
  • 空间复杂度控制在O(M),M为结果对象的属性总数,除必要的存储/递归栈外无额外开销
  • 采用JS引擎高度优化的原生操作,避免高开销API调用
  • 仅当两侧同键的值均为非null、非数组的普通对象时才递归对比,其余场景直接判断相等性/复制原值
  • 分两次遍历:先处理第一个对象(schema)的所有属性,再遍历第二个对象的独有属性直接复制,避免重复判断

递归版(常规场景性能最优)

适合嵌套深度小于1000层的绝大多数业务场景,V8引擎对递归的优化程度很高,性能优于迭代实现:

function fastDeepCompare(schema, doc) {
  const result = {}
  let key, val1, val2
  const hasOwn = Object.prototype.hasOwnProperty

  // 遍历schema侧属性
  for (key in schema) {
    if (!hasOwn.call(schema, key)) continue
    val1 = schema[key]
    val2 = doc?.[key]

    // 仅当两侧值均为普通对象时递归
    const isVal1Plain = typeof val1 === 'object' && val1 !== null && !Array.isArray(val1)
    const isVal2Plain = typeof val2 === 'object' && val2 !== null && !Array.isArray(val2)

    if (isVal1Plain && isVal2Plain) {
      result[key] = fastDeepCompare(val1, val2)
    } else if (doc != null && hasOwn.call(doc, key)) {
      // 两侧均有该属性,返回相等判断结果
      result[key] = val1 === val2
    } else {
      // 仅schema存在该属性,直接复制原值
      result[key] = val1
    }
  }

  // 遍历doc侧独有属性,直接复制
  if (doc != null) {
    for (key in doc) {
      if (!hasOwn.call(doc, key) || hasOwn.call(schema, key)) continue
      result[key] = doc[key]
    }
  }

  return result
}

迭代版(适配超深嵌套大对象)

如果需要处理嵌套深度超过JS递归栈上限(通常为1000~10000层)的超大对象,可以用显式栈替代递归,无栈溢出风险,性能和递归版几乎一致:

function fastDeepCompareIterative(schema, doc) {
  const root = {}
  const stack = [[schema, doc, root]]
  const hasOwn = Object.prototype.hasOwnProperty

  while (stack.length) {
    const [curSchema, curDoc, curRes] = stack.pop()
    let key, val1, val2

    // 处理当前层schema属性
    for (key in curSchema) {
      if (!hasOwn.call(curSchema, key)) continue
      val1 = curSchema[key]
      val2 = curDoc?.[key]

      const isVal1Plain = typeof val1 === 'object' && val1 !== null && !Array.isArray(val1)
      const isVal2Plain = typeof val2 === 'object' && val2 !== null && !Array.isArray(val2)

      if (isVal1Plain && isVal2Plain) {
        curRes[key] = {}
        stack.push([val1, val2, curRes[key]])
      } else if (curDoc != null && hasOwn.call(curDoc, key)) {
        curRes[key] = val1 === val2
      } else {
        curRes[key] = val1
      }
    }

    // 处理当前层doc独有属性
    if (curDoc != null) {
      for (key in curDoc) {
        if (!hasOwn.call(curDoc, key) || hasOwn.call(curSchema, key)) continue
        curRes[key] = curDoc[key]
      }
    }
  }

  return root
}

循环选型说明

for...in循环是当前遍历对象属性性能最高的方式:

  • 相比Object.keys() + forEach/for of,不需要先生成键名的中间数组,减少了内存分配和遍历开销,在现代JS引擎中性能高20%~50%
  • 引擎对for...in的遍历做了惰性优化,不会一次性加载所有键名到内存,大对象场景内存表现更好
  • 仅需要通过hasOwnProperty过滤原型链上的继承属性即可,开销极低

性能测试方法

要准确测试不同实现的性能差异,按照以下步骤操作,避免JIT优化干扰结果:

  • 构造贴近业务场景的测试用例,建议覆盖三类场景:
    • 浅层次大对象:万级平级属性,30%为双方共有属性,70%为单侧独有
    • 深层次对象:嵌套深度≥100层,每层10个左右属性
    • 混合类型对象:包含数组、数字、字符串、null、普通对象等多种类型值
  • 预热阶段:先循环执行待测试函数10~20次,触发JS引擎的JIT编译优化
  • 正式计时:用performance.now()做高精度计时,循环执行测试函数100~1000次(保证总执行时间≥100ms,减少计时误差),计算单次平均执行时间
  • 测试时关闭调试工具、其他占用CPU的程序,同一环境下多次测试取平均值

简单测试代码示例:

// 生成1w属性的测试对象
function genTestObj(prefix) {
  const obj = {}
  for (let i = 0; i < 10000; i++) {
    if (i % 3 === 0) obj[`common${i}`] = i % 2 ? `str${i}` : i
    else obj[`${prefix}${i}`] = i
  }
  obj.nested = { a:1, b: { c:2, d: 'test' } }
  return obj
}
const schema = genTestObj('schema_')
const doc = genTestObj('doc_')

// 预热
for (let i = 0; i < 20; i++) fastDeepCompare(schema, doc)

// 计时
const loop = 100
const start = performance.now()
for (let i = 0; i < loop; i++) fastDeepCompare(schema, doc)
console.log(`单次平均耗时:${((performance.now() - start)/loop).toFixed(3)}ms`)

注:数组、Date、RegExp等非普通对象类型不会递归对比,共有属性直接判断引用相等,单侧存在则直接复制原值,完全匹配需求示例的逻辑。

内容的提问来源于stack exchange,提问作者ColdHands

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:15:45