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

如何在TypeScript中对比嵌套数据结构识别属性值变更?

TypeScript 嵌套对象与数组变更对比实现

核心实现函数

下面是一个能递归处理嵌套对象、数组,并保留变更结构的TypeScript函数,同时支持识别字段新增、删除和修改:

type DiffResult = {
  [key: string]: any;
};

function isObject(value: any): value is Record<string, any> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

function diffObjects(oldObj: any, newObj: any): DiffResult {
  const diff: DiffResult = {};

  // 遍历旧对象的所有键,检查是否被修改或删除
  Object.keys(oldObj).forEach(key => {
    const oldValue = oldObj[key];
    const newValue = newObj[key];

    // 基本类型对比
    if (oldValue !== newValue) {
      if (isObject(oldValue) && isObject(newValue)) {
        // 嵌套对象递归对比
        const nestedDiff = diffObjects(oldValue, newValue);
        if (Object.keys(nestedDiff).length > 0) {
          diff[key] = nestedDiff;
        }
      } else if (Array.isArray(oldValue) && Array.isArray(newValue)) {
        // 数组对比:逐元素递归检查,或直接判断是否相等
        const arrayDiff = diffArrays(oldValue, newValue);
        if (arrayDiff !== null) {
          diff[key] = arrayDiff;
        }
      } else {
        // 字段修改或删除(newValue可能为undefined)
        diff[key] = newValue;
      }
    }
  });

  // 遍历新对象的键,检查是否有新增字段
  Object.keys(newObj).forEach(key => {
    if (!(key in oldObj)) {
      diff[key] = newObj[key];
    }
  });

  return diff;
}

function diffArrays(oldArr: any[], newArr: any[]): any[] | null {
  // 如果数组长度不同,直接返回新数组
  if (oldArr.length !== newArr.length) {
    return newArr;
  }

  // 逐元素对比,收集变更
  const hasChanges = oldArr.some((item, index) => {
    const newItem = newArr[index];
    if (item === newItem) return false;
    if (isObject(item) && isObject(newItem)) {
      return Object.keys(diffObjects(item, newItem)).length > 0;
    }
    if (Array.isArray(item) && Array.isArray(newItem)) {
      return diffArrays(item, newItem) !== null;
    }
    return true;
  });

  return hasChanges ? newArr : null;
}

示例验证

用你提供的测试对象运行函数:

const oldObject = { name: 'John', age: 30, city: 'New York', friends: ['ali', 'qasim'], def: { 'a': 1, 'b': 2 } };
const newObject = { name: 'John', age: 35, city: 'New York', friends: ['ali', 'haider'] };

const result = diffObjects(oldObject, newObject);
console.log(result);

输出结果:

{
  age: 35,
  friends: ['ali', 'haider'],
  def: undefined
}

关键逻辑说明

  1. 基本类型处理:直接对比值,不同则记录新值(包括字段被删除的情况,新值为undefined)
  2. 嵌套对象处理:递归调用diffObjects,只有当嵌套对象存在变更时,才将该嵌套对象的变更结果加入最终diff
  3. 数组处理:
    • 若数组长度不同,直接返回新数组(视为整体变更)
    • 长度相同时逐元素递归对比,只要有元素变更,就返回整个新数组(也可修改为只返回变更的元素索引,按需调整)
  4. 新增字段识别:遍历新对象的键,若旧对象中不存在该键,直接记录为新增字段

优化建议

  1. 循环引用处理:如果对象存在循环引用,需要添加缓存(比如用WeakMap记录已对比过的对象对),避免无限递归:
    function diffObjects(oldObj: any, newObj: any, cache = new WeakMap()): DiffResult {
      if (cache.has(oldObj) && cache.get(oldObj) === newObj) return {};
      cache.set(oldObj, newObj);
      // 原有逻辑...
    }
    
  2. 特殊类型支持:针对Date、RegExp等特殊对象,需要单独对比其值而非引用:
    function isSpecialType(value: any): boolean {
      return value instanceof Date || value instanceof RegExp;
    }
    
    // 在基本类型对比前添加:
    if (isSpecialType(oldValue) && isSpecialType(newValue)) {
      if (oldValue.toString() !== newValue.toString()) {
        diff[key] = newValue;
      }
      return;
    }
    
  3. 数组精细化对比:如果需要精确识别数组中新增、删除、修改的元素,可以引入索引+值的双重对比逻辑,但会增加复杂度
  4. 性能优化:对于大型对象,可以通过提前判断对象类型、减少不必要的递归调用提升效率

内容的提问来源于stack exchange,提问作者TAYYAB-IT

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:52:55