如何在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 }
关键逻辑说明
- 基本类型处理:直接对比值,不同则记录新值(包括字段被删除的情况,新值为
undefined) - 嵌套对象处理:递归调用
diffObjects,只有当嵌套对象存在变更时,才将该嵌套对象的变更结果加入最终diff - 数组处理:
- 若数组长度不同,直接返回新数组(视为整体变更)
- 长度相同时逐元素递归对比,只要有元素变更,就返回整个新数组(也可修改为只返回变更的元素索引,按需调整)
- 新增字段识别:遍历新对象的键,若旧对象中不存在该键,直接记录为新增字段
优化建议
- 循环引用处理:如果对象存在循环引用,需要添加缓存(比如用
WeakMap记录已对比过的对象对),避免无限递归:function diffObjects(oldObj: any, newObj: any, cache = new WeakMap()): DiffResult { if (cache.has(oldObj) && cache.get(oldObj) === newObj) return {}; cache.set(oldObj, newObj); // 原有逻辑... } - 特殊类型支持:针对
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; } - 数组精细化对比:如果需要精确识别数组中新增、删除、修改的元素,可以引入索引+值的双重对比逻辑,但会增加复杂度
- 性能优化:对于大型对象,可以通过提前判断对象类型、减少不必要的递归调用提升效率
内容的提问来源于stack exchange,提问作者TAYYAB-IT
相关产品推荐
相关产品推荐

