我找到了符合我需求的(个人)方便的答案: https://stackoverflow.com/a/6713782/2678218
但由于我使用 TypeScript ,我可以使用泛型:
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
语句。但我不知道要拿走哪一个,也不确定是否会有更优的代码。所以我将问题留在这里,让所有人投票给出最佳答案。
在这种情况下,两个对象相等的实际意思是两个相同类型的对象具有相同的每个属性值。
答案 0 :(得分:2)
@Lostfields提到有人可以为any
类型传递T
,但这并不是一个大问题,因为使用any
告诉编译器不要键入check任何东西。如果这会导致运行时出现不良行为,我将负责处理any
中传递的代码,而不是equals()
内的代码。类型系统的一个用途确实是消除了一些不必要的运行时检查,但需要注意的是,您仍需要对从不受信任的来源传入的任何数据进行清理。您是否正在构建一个可能不会使用TypeScript的开发人员使用的库?然后不要放松任何运行时检查。您是在构建要在内部使用的代码还是依赖于您的打字的其他TypeScript开发人员?然后通过各种手段消除不必要的检查。
话虽如此,我不能删除该实现中的许多检查。在运行时,检查的每个条件都可能是true
或false
,即使已知TypeScript已确定x
和y
属于同一类型。 (在下文中,我将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
相同。但是,有人可能会{I}传递TypeScript中的类型检查。如果你关心防范它,那就由你来决定。
equals(0, new Number(0))
:两个结构相同的类别为假,例如(x.constructor !== y.constructor)
。如果您不担心结构相同但不同的类,则可以取消此检查。
class A{}; class B{}; equals(new A(), new B())
:此检查与TypeScript无关;它必须留下来。
对于下一个案例,请考虑
(!x.hasOwnProperty(p))
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))
的实例或具有可选属性的任何类型,这些实例可能为true或false。或者没有可选属性的任何类型的子类型,因为TypeScript中允许使用额外的属性。
Foo
,(x[p] === y[p])
,(typeof (x[p]) !== 'object')
:出于与上述相同的原因,这些可能是真或假,可以通过传入具有单个属性的类型来看出上面提到的类型。也就是说,如果(!equals(x[p], y[p]))
需要运行时检查,那么equals(x,y)
将需要相同的运行时检查。
所以,它取决于你。随意离开实施。毕竟,额外的运行时检查不会伤害任何东西。如果您认为自己不需要,请随意删除一些支票;再一次,你是唯一一个知道equals({foo: x},{foo: y})
用户会有多疯狂的人。例如,你会怎么做:
equals()
你关心循环引用吗?如果没有,请不要担心。如果是这样,那么你需要强化你的平等检查来处理它。
希望有所帮助;祝你好运!