这里有一个解决方案:
TypeScript(但很容易转换为JavaScript)
没有lib依赖项
泛型的,不关心检查对象类型(除了对象类型)
支持值为undefined的属性
深度不(默认)
首先,我们定义比较结果接口:
export interface ObjectDiff {
added: {} | ObjectDiff;
updated: {
[propName: string]: Update | ObjectDiff;
};
removed: {} | ObjectDiff;
unchanged: {} | ObjectDiff;
}
对于change的特殊情况,我们想知道什么是旧值和新值:
export interface Update {
oldValue: any;
newValue: any;
}
然后我们可以提供只有两个循环的diff函数(如果deep为真,则具有递归性):
export class ObjectUtils {
/**
* @return if obj is an Object, including an Array.
*/
static isObject(obj: any) {
return obj !== null && typeof obj === 'object';
}
/**
* @param oldObj The previous Object or Array.
* @param newObj The new Object or Array.
* @param deep If the comparison must be performed deeper than 1st-level properties.
* @return A difference summary between the two objects.
*/
static diff(oldObj: {}, newObj: {}, deep = false): ObjectDiff {
const added = {};
const updated = {};
const removed = {};
const unchanged = {};
for (const oldProp in oldObj) {
if (oldObj.hasOwnProperty(oldProp)) {
const newPropValue = newObj[oldProp];
const oldPropValue = oldObj[oldProp];
if (newObj.hasOwnProperty(oldProp)) {
if (newPropValue === oldPropValue) {
unchanged[oldProp] = oldPropValue;
} else {
updated[oldProp] = deep && this.isObject(oldPropValue) && this.isObject(newPropValue) ? this.diff(oldPropValue, newPropValue, deep) : {newValue: newPropValue};
}
} else {
removed[oldProp] = oldPropValue;
}
}
}
for (const newProp in newObj) {
if (newObj.hasOwnProperty(newProp)) {
const oldPropValue = oldObj[newProp];
const newPropValue = newObj[newProp];
if (oldObj.hasOwnProperty(newProp)) {
if (oldPropValue !== newPropValue) {
if (!deep || !this.isObject(oldPropValue)) {
updated[newProp].oldValue = oldPropValue;
}
}
} else {
added[newProp] = newPropValue;
}
}
}
return {added, updated, removed, unchanged};
}
}
例如,调用:
ObjectUtils.diff(
{
a: 'a',
b: 'b',
c: 'c',
arr: ['A', 'B'],
obj: {p1: 'p1', p2: 'p2'}
},
{
b: 'x',
c: 'c',
arr: ['B', 'C'],
obj: {p2: 'p2', p3: 'p3'},
d: 'd'
},
);
将返回:
{
added: {d: 'd'},
updated: {
b: {oldValue: 'b', newValue: 'x'},
arr: {oldValue: ['A', 'B'], newValue: ['B', 'C']},
obj: {oldValue: {p1: 'p1', p2: 'p2'}, newValue: {p2: 'p2', p3: 'p3'}}
},
removed: {a: 'a'},
unchanged: {c: 'c'},
}
对deep third参数调用同样的方法将返回:
{
added: {d: 'd'},
updated: {
b: {oldValue: 'b', newValue: 'x'},
arr: {
added: {},
removed: {},
unchanged: {},
updated: {
0: {oldValue: 'A', newValue: 'B'},
1: {oldValue: 'B', newValue: 'C', }
}
},
obj: {
added: {p3: 'p3'},
removed: {p1: 'p1'},
unchanged: {p2: 'p2'},
updated: {}
}
},
removed: {a: 'a'},
unchanged: {c: 'c'},
}