Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/369.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript对象值相等测试?_Javascript_Coffeescript_Coding Style_Refactoring - Fatal编程技术网

Javascript对象值相等测试?

Javascript对象值相等测试?,javascript,coffeescript,coding-style,refactoring,Javascript,Coffeescript,Coding Style,Refactoring,我正在编写一个Javascript程序,程序状态的一部分是一个对象。我想定期计算状态并将其与当前状态进行比较,以确定是否应触发事件,下面是一些伪代码: var newState = calculateState(); if (newState !== oldState) { triggerEvent(); } 然而,由于状态是一个对象,我需要进行值相等性检查以及防止空引用检查。也就是说,如果状态从非null值变为null,或者反过来,我还需要触发事件: if (newState ===

我正在编写一个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)));
}

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

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)));
}
让我们对此进行分解(我使用了
n
o
速记,以便在一行中显示更多内容,以便更容易看到正在发生的事情,而不是建议使用这种简洁的约定):

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

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

因此,我们来到这里:

if (n !== o || 
   ((n !== null && o !== null) && 
    (n.age !== o.age || n.name !== o.name))) {
    triggerEvent();
}
为了简化,通常需要引入函数。如果您经常做这类事情,为这些状态提供一个相等的方法,比较这些
age
name
字段,可能会更好。如果您添加了新的状态,它还可以减少代码扩展的错误倾向

零交换技巧

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

// 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();
    }
}
这是简化还是复杂化很大程度上取决于您使用这种简洁的替换一行代码的频率。如果这是非常罕见的,那可能只会增加混乱。但这种交换技术允许您编写稍微详细一些的代码,以换取可能更简单、不会让您的大脑负担过重的代码(有时更简单的代码比需要嵌套的非常人工解析的非常复杂的代码更可取)

让我们对此进行分解(我使用了
n
o
速记,以便在一行中显示更多内容,以便更容易看到正在发生的事情,而不是建议使用这种简洁的约定):

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

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

因此,我们来到这里:

if (n !== o || 
   ((n !== null && o !== null) && 
    (n.age !== o.age || n.name !== o.name))) {
    triggerEvent();
}
为了简化,通常需要引入函数。如果您经常做这类事情,为这些状态提供一个相等的方法,比较这些
age
name
字段,可能会更好。如果您添加了新的状态,它还可以减少代码扩展的错误倾向

零交换技巧

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

// 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();
    }
}

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

在我的脑海中,您可以尝试在两个对象上使用
JSON.stringify
,并将它们简单地作为字符串进行比较。如果(!newState | | | newState)&&&(!oldState | | oldState)&&&(newState&&oldState)){if(newState.name!==oldState.name){triggerEvent()},您可以尝试使用此
@1cgonza但
JSON.stringify
是否说明了
对象中键的顺序?即使在ES6中,键是按顺序排列的,您真的希望
{a:1,b:2}
{b:2,a:1}
不相等吗?@muistooshort我同意您的看法,这会有问题。谢谢你指出这一点。从我的头顶上看,你可以尝试在两个对象上使用
JSON.stringify
,并将它们简单地作为字符串进行比较。如果(!newState | | newState)&&(!oldState | oldState)&&(newState&&oldState)){if(newState.name!==oldState.name){triggerEvent()},你能试试这个
@1cgonza但
JSON.stringify
是否说明了
对象中键的顺序?即使在ES6中,键是按顺序排列的,您真的希望
{a:1,b:2}
{b:2,a:1}
不相等吗?@muistooshort我同意您的看法,这会有问题。谢谢你指出。是的,把它们放到函数中是个好主意。在这个阶段,使用空交换技巧对我来说太难了,因为我确信两周后我将无法理解它们:谢谢!是的,把它们放到函数中是个好主意。在这个阶段,使用空交换技巧对我来说太难了,因为我确信两周后我将无法理解它们:谢谢!