Javascript对象值相等测试

Javascript object value equality test?

本文关键字:测试 对象 Javascript      更新时间:2023-09-26

我正在编写一个Javascript程序,程序状态的一部分是对象。我想定期计算状态,并将其与当前状态进行比较,以决定是否应该触发事件,下面是一些伪代码:

var newState = calculateState();
if (newState !== oldState) {
  triggerEvent();
}

然而,由于状态是一个对象,我需要进行值相等性检查以及防止空引用检查。也就是说,如果状态从非null值变为null或相反,我还需要触发事件:

if (newState === null && oldState !== null ||  // non null -> null
    newState !== null && oldState === null ||  // null -> non null
    (newState !== null && oldState !== null &&  // both non null, check members
     (newState.age !== oldState.age ||
      newState.name !== oldState.name))) {
    triggerEvent();
}

正如您所看到的,代码非常混乱和丑陋。

有没有一种方法可以在Javascript中重构它?

谢谢!

如果是为了更干净的代码,我建议在单独的函数中处理两个对象之间的比较,如下所示:

ar newState = calculateState();
// areEqual() takes the two objects as parameters
if (areEqual(oldState, newState)) {
  triggerEvent();
}

然后决定两个对象在该函数中是否相等

// This function compares two state objects and returns true if they are valid and different
function areEqual(oldState, newState){
    return (newState === null && oldState !== null ||  // non null -> null
        newState !== null && oldState === null ||  // null -> non null
        (newState !== null && oldState !== null &&  // both non null, check members
        (newState.age !== oldState.age || newState.name !== oldState.name)));
}
if (n === null && o !== null ||  // non null -> null
    n !== null && o === null ||  // null -> non null
    (n !== null && o !== null &&  // both non null, check members
     (n.age !== o.age ||
      n.name !== o.name))) {
    triggerEvent();
}

让我们来分解一下(我使用了no短手来在一条线上放置更多的东西,这样更容易看到发生了什么——而不是建议使用这个简洁的约定):

n === null && o !== null ||  
n !== null && o === null

这个表达式可以简化为:n !== o

这是因为如果其中一个为null,而另一个不为null,则表达式将计算true。如果对象指向相同的内存地址,这也会返回false(这是一件好事,因为它们的字段也会匹配,在这种情况下我们不想触发事件)。

因此,我们来到这里:

if (n !== o || 
   ((n !== null && o !== null) && 
    (n.age !== o.age || n.name !== o.name))) {
    triggerEvent();
}

为了简化,通常需要引入函数。如果你经常做这类事情,为这些状态提供一个相等的方法,比较这些agename字段,那可能会很好。如果您添加新的状态,它还将使代码不太容易扩展错误。

空交换技巧

另一个有时会以几个周期为代价的有用技巧是对之间的零交换。示例:

// if 'a' is null:
if (!a)
{
    // swap it with 'b'.
    a = [b, b = a][0];
}
// Now if 'a' is still null, both values are null.
// If 'b' is not null, both values are not null.

我们可以在上面的代码中这样利用它:

local state1 = oldState
local state2 = newState
if (!state1)
    state1 = [state2, state2 = state1][0];
if (state1) {
    // If only one of these is not null or their fields don't match:
    if (!state2 || state1.age !== state2.age || state1.name !== state2.name) {
        triggerEvent();
    }
}

这是简化还是复杂,很大程度上取决于你使用这种简洁的交换一行的频率。如果这种情况非常罕见,那可能只会加剧混乱。但这种交换技术可以让你写稍微更详细的代码,以换取可能更直接、不会让你的大脑堆栈不堪重负的代码(有时更简单的代码比一些需要嵌套的、非常人性化的解析的非常复杂的东西更可取)。