TypeScript:对象相等性比较(对象等于对象)

TypeScript : Object Equality Comparison (Object Equals Object)

我发现这个(个人)方便的答案符合我的需要:

但是因为我使用的是 TypeScript,所以我可以使用 Generics:

private equals<T>(x: T, y: T) {
    if (x === y) {
        return true; // if both x and y are null or undefined and exactly the same
    } else if (!(x instanceof Object) || !(y instanceof Object)) {
        return false; // if they are not strictly equal, they both need to be Objects
    } else if (x.constructor !== y.constructor) {
        // they must have the exact same prototype chain, the closest we can do is
        // test their constructor.
        return false;
    } else {
        for (const p in x) {
            if (!x.hasOwnProperty(p)) {
                continue; // other properties were tested using x.constructor === y.constructor
            }
            if (!y.hasOwnProperty(p)) {
                return false; // allows to compare x[ p ] and y[ p ] when set to undefined
            }
            if (x[p] === y[p]) {
                continue; // if they have the same strict value or identity then they are equal
            }
            if (typeof (x[p]) !== 'object') {
                return false; // Numbers, Strings, Functions, Booleans must be strictly equal
            }
            if (!this.equals(x[p], y[p])) {
                return false;
            }
        }
        for (const p in y) {
            if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
                return false;
            }
        }
        return true;
    }
}

我敢肯定,因为我们在这里使用 <T>,所以我们可以重构代码。可以肯定的是删除一些不再需要的 if 语句。但是我不确定要带走哪个,也不确定是否会有更优化的代码。所以我把问题留在这里,让大家投票选出最佳答案。

在这种情况下,我所说的两个对象相等实际上是指两个相同类型的对象在每个 属性.

上具有相等的值。

@Lostfields 提到有人可以为类型 T 传递 any,但这不是什么大问题,因为使用 any 是告诉编译器不要进行任何类型检查。如果这导致运行时出现不良行为,我会将处理此问题的责任放在传入 any 的代码上,而不是 equals() 中的代码。类型系统的一种用途确实是消除一些不必要的运行时检查,但需要注意的是,您仍然需要清理从不受信任的来源传入的任何数据。您是否正在构建一个库,供甚至可能不使用 TypeScript 的开发人员使用?然后不要放松任何运行时检查。你构建的代码是供内部使用还是供依赖于你的类型的其他 TypeScript 开发人员使用?那么一定要去掉不必要的检查。


也就是说,我不认为您可以删除该实施中的许多检查。每个检查的条件在运行时可能是 truefalse,即使知道 TypeScript 已决定 xy 属于同一类型。 (在下文中,我将 equals() 视为独立函数而不是方法。添加 this 或您认为合适的任何对象名称)

让我们逐一检查:

  • (x === y)equals(x,x) 为真,equals(x, Object.assign({},x)) 为假。这个必须留下来[=44​​=]

  • ((!(x instanceof Object) || !(y instanceof Object)):这个你 可能 决定只用 (!(x instanceof Object)) 替换,因为实际上 TypeScript 中的类型是Object 或不是,因此 x instanceof Object 应该与 y instanceof Object 相同。不过,有人可能会做 equals(0, new Number(0)) 来通过 TypeScript 中的类型检查。这取决于你是否关心防范。

  • (x.constructor !== y.constructor):假为两个结构相同的类,如class A{}; class B{}; equals(new A(), new B())。如果您不担心结构相同但不同 类,则可以取消此检查。

  • (!x.hasOwnProperty(p)):此检查与TypeScript无关;它必须留下来。

下一个案例,考虑

interface Foo { foo?: string, bar: number, baz?: boolean };
const x: Foo = { foo: 'hello', bar: 12 };
const y: Foo = { bar: 12, baz: false };
equals(x, y);
  • (!y.hasOwnProperty(p))(y.hasOwnProperty(p) && !x.hasOwnProperty(p)):对于 Foo 的实例或具有可选属性的任何类型,它们可以是 true 或 false。或者没有可选属性的类型的任何子类型,因为 TypeScript 允许额外的属性。

  • (x[p] === y[p])(typeof (x[p]) !== 'object')(!equals(x[p], y[p])):这些可以是true也可以是false,原因同上,传入一个类型就可以看出使用上述类型中的单个 属性。也就是说,如果 equals(x,y) 需要运行时检查,那么 equals({foo: x},{foo: y}) 将需要相同的运行时检查。


所以,这取决于你。随意离开实现。毕竟,额外的运行时检查不会 伤害 任何东西。如果您认为不需要,请随意删除一些支票;同样,您是唯一知道 equals() 的用户会有多疯狂的人。例如,你会怎么做:

interface Ouroboros {
    prop: Ouroboros;
}
let x = {} as Ouroboros;
x.prop = x;

let y = {} as Ouroboros;
y.prop = y;

console.log(equals(x,y))

你关心循环引用吗?如果没有,请不要担心。如果是这样,那么您需要加强平等检查来处理它。


希望对您有所帮助;祝你好运!