简体   繁体   English

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

[英]TypeScript : Object Equality Comparison (Object Equals Object)

I have found this (personally) convenient answer that fits my needs: https://stackoverflow.com/a/6713782/2678218 我已经找到了适合我需求的(个人)便捷答案: https : //stackoverflow.com/a/6713782/2678218

But since I am using TypeScript , I can have something like this with Generics : 但是由于我使用的是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;
    }
}

I'm sure that since we're using <T> here, we can refactor the code. 我敢肯定,因为我们在这里使用<T> ,所以我们可以重构代码。 One thing's for sure is by removing some if statements that are not needed anymore. 可以肯定的是,删除一些不再需要的if语句。 But i am not confident of which to take away and also not sure if there will be a more optimal code for this. 但是我不知道该拿走什么,也不确定是否会有更好的代码。 So I'll leave the question here and have everyone vote for the best answer. 因此,我将问题留在这里,让大家投票给最佳答案。

In this context, what I actually mean by equality of two objects is when two objects of the same type have equal values of every property. 在这种情况下,两个对象相等实际上是指相同类型的两个对象在每个属性上具有相等的值。

@Lostfields mentions that someone could pass any for the type T , but that's not a big concern, since using any is telling the compiler not to type check anything. @Lostfields提到有人可以为T类型传递any值,但这不是什么大问题,因为使用any会告诉编译器不要对任何内容进行类型检查。 If this leads to undesirable behavior at runtime, I'd place the responsibility for dealing with this on the code that passed in any , instead of the code inside equals() . 如果这在运行时导致不良行为,我将负责处理此问题的代码传递给any ,而不是equals()内部的代码。 One use of the type system is indeed to eliminate some unnecessary runtime checks, with the caveat that you still need to do sanitize any data being passed in from untrusted sources. 类型系统的一种用途确实是消除了一些不必要的运行时检查,但需要注意的是,您仍然需要清理从不可信来源传入的所有数据。 Are you building a library that will be used by developers who might not even be using TypeScript? 您是否正在建立一个供甚至可能不使用TypeScript的开发人员使用的库? Then do not relax any runtime checks. 然后不要放松任何运行时检查。 Are you building code to be used internally or by other TypeScript developers who depend on your typings? 您是在构建供内部使用还是由其他TypeScript开发人员使用(取决于您的键入)的代码? Then by all means eliminate unnecessary checks. 然后一定要消除不必要的检查。


That being said, I don't you can remove many of the checks in that implementation. 话虽这么说,但我不能删除该实现中的许多检查。 Each of the conditionals checked might be true or false at runtime, even knowing that TypeScript has decided that x and y are of the same type. 即使知道TypeScript已确定xy属于同一类型,所检查的每个条件在运行时也可能为truefalse (In what follows I will be treating equals() as a standalone function instead of a method. Add this or whatever the object name is as you see fit) (在接下来的内容中,我将equals()作为独立函数而不是方法来对待。添加this名称或您认为合适的任何对象名称)

Let's examine each one: 让我们检查每个:

  • (x === y) : True for equals(x,x) , false for equals(x, Object.assign({},x)) . (x === y) :对于equals(x,x)为true,对于equals(x, Object.assign({},x))为false。 This one has to stay. 这一个必须留下。

  • ((!(x instanceof Object) || !(y instanceof Object)) : This one you might decide to replace with just (!(x instanceof Object)) , since in practice a type in TypeScript is either an Object or it is not, and so x instanceof Object should be the same as y instanceof Object . Still, someone might do equals(0, new Number(0)) which passes the type check in TypeScript. It's up to you if you care about guarding against that. ((!(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) : False for two structurally identical classes, such as class A{}; class B{}; equals(new A(), new B()) (x.constructor !== y.constructor) :对于两个结构相同的类(例如class A{}; class B{}; equals(new A(), new B())(x.constructor !== y.constructor)为False class A{}; class B{}; equals(new A(), new B()) class A{}; class B{}; equals(new A(), new B()) class A{}; class B{}; equals(new A(), new B()) . class A{}; class B{}; equals(new A(), new B()) If you're not worried about structurally identical but distinct classes, you can eliminate this check. 如果您不担心结构相同但又不同的类,则可以取消此检查。

  • (!x.hasOwnProperty(p)) : This check has nothing to do with TypeScript; (!x.hasOwnProperty(p)) :此检查与TypeScript无关; it has to stay. 它必须留下。

For the next case, consider 对于下一种情况,请考虑

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)) and (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) : These could be true or false for instances of Foo , or any type with optional properties. (!y.hasOwnProperty(p))(y.hasOwnProperty(p) && !x.hasOwnProperty(p)) :对于Foo实例或具有可选属性的任何类型,它们可以为true或false。 Or any subtype of a type without optional properties, since extra properties are allowed in TypeScript. 或没有可选属性的类型的任何子类型,因为TypeScript允许额外的属性。

  • (x[p] === y[p]) , (typeof (x[p]) !== 'object') , (!equals(x[p], y[p])) : These can be true or false for the same reasons as above, which can be seen by passing in a type with a single property of the types mentioned above. (x[p] === y[p])(typeof (x[p]) !== 'object')(!equals(x[p], y[p])) :这些可以成立或false(与上述相同),可以通过传入具有上述类型的单个属性的类型来看出。 That is, if equals(x,y) needs a runtime check, then equals({foo: x},{foo: y}) will need the same runtime check. 也就是说,如果equals(x,y)需要运行时检查,那么equals({foo: x},{foo: y})将需要相同的运行时检查。


So, it's up to you. 因此,取决于您。 Feel free to leave the implementation alone. 随意离开实施。 Extra runtime checks don't hurt anything, after all. 毕竟,额外的运行时检查不会对您造成任何伤害 Feel free to remove some checks if you don't think you'll need them; 如果您认为不需要,可以随时删除一些支票; again, you're the only one who knows how crazy the users of equals() will be. 同样,您是唯一知道equals()用户将变得多么疯狂的人。 For example, what would you do about this: 例如,您将如何处理:

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

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

console.log(equals(x,y))

Do you care about circular references? 您是否关心循环引用? If not, don't worry. 如果没有,请不要担心。 If so, then you need to harden your equality check to deal with it. 如果是这样,那么您需要加强相等性检查以对其进行处理。


Hope that helps; 希望能有所帮助; good luck! 祝好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM