这是一个解决方案:
- 打字稿(但可以轻松转换为Javascript)
- 没有库依赖
- 通用的,不关心检查对象类型(除了
object
类型)
- 支持有价值的属性
undefined
- 不深(默认)
首先我们定义比较结果接口:
export interface ObjectComparison {
added: {};
updated: {
[propName: string]: Change;
};
removed: {};
unchanged: {};
}
在变更的特殊情况下,我们想知道什么是旧值和新值:
export interface Change {
oldValue: any;
newValue: any;
}
然后,我们可以提供diff
仅两个循环的函数(如果deep
为则具有递归性true
):
export class ObjectUtils {
static diff(o1: {}, o2: {}, deep = false): ObjectComparison {
const added = {};
const updated = {};
const removed = {};
const unchanged = {};
for (const prop in o1) {
if (o1.hasOwnProperty(prop)) {
const o2PropValue = o2[prop];
const o1PropValue = o1[prop];
if (o2.hasOwnProperty(prop)) {
if (o2PropValue === o1PropValue) {
unchanged[prop] = o1PropValue;
} else {
updated[prop] = deep && this.isObject(o1PropValue) && this.isObject(o2PropValue) ? this.diff(o1PropValue, o2PropValue, deep) : {newValue: o2PropValue};
}
} else {
removed[prop] = o1PropValue;
}
}
}
for (const prop in o2) {
if (o2.hasOwnProperty(prop)) {
const o1PropValue = o1[prop];
const o2PropValue = o2[prop];
if (o1.hasOwnProperty(prop)) {
if (o1PropValue !== o2PropValue) {
if (!deep || !this.isObject(o1PropValue)) {
updated[prop].oldValue = o1PropValue;
}
}
} else {
added[prop] = o2PropValue;
}
}
}
return { added, updated, removed, unchanged };
}
/**
* @return if obj is an Object, including an Array.
*/
static isObject(obj: any) {
return obj !== null && typeof obj === 'object';
}
}
例如,调用:
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
第三个参数调用它将返回:
{
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'},
}