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

Col*_*rus 6 optimization compare equality typescript ecmascript-6

我找到了这个(个人)方便的答案,满足我的需求: /sf/answers/469964771/

但由于我使用的是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

jca*_*alz 5

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


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

让我们逐一检查一下:

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

  • ((!(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);
Run Code Online (Sandbox Code Playgroud)
  • (!y.hasOwnProperty(p))和:对于或具有可选属性的任何类型的(y.hasOwnProperty(p) && !x.hasOwnProperty(p))实例,这些可能是 true 或 false 。Foo或者没有可选属性的类型的任何子类型,因为 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))
Run Code Online (Sandbox Code Playgroud)

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


希望有帮助;祝你好运!