两个相等数组的比较失败

Comparison of two equal arrays fails

通常,在向 Whosebug 社区发表讲话时,我会尽可能详细地描述我的问题。
但是现在很难说什么。看看下面的图片:

总的来说,colors在很多方面都改了很多次;而 initial 是常量,用于检查当前状态是否等于初始状态(如果不等于则取消所有更改)。
这个问题看起来很简单,但我坚持了下来。怎么可能,两个相等的数组并不总是相等?

没有带上整个项目的代码,我不知道要添加什么,所以尽管问,我会回答。

JavaScript对象比较(包括数组对象之间的比较)只是比较引用值。两个不同的对象永远不会彼此相等,无论它们看起来如何。

您当然可以根据自己的标准编写自己的数组比较函数,了解一个数组与另一个数组相等的意义。例如,如果数组应包含相同顺序的相同值才被视为相等,则:

function arraysEq(a1, a2) {
  if (a1.length != a2.length) return false;
  for (var i = 0; i < a1.length; ++i)
    if (a1[i] !== a2[i]) return false;
  return true;
}

请注意,这只是一个示例;它不应被视为 "how do I compare two arrays" 问题的通用解决方案。

ECMAscript 中的数组也只是对象的一种特殊形式。这反过来意味着,如果您使用 =====array1array2 进行比较,您实际上是在比较两个 对象引用 ,不是数组本身,也不是它们的内容。

实际上,如果您可以保证内容始终是字符串,那么您通过 join 对数组进行比较并比较结果的第二种方法也不错。否则,您将不得不循环数组,单独比较每个值,当然还要比较这些数组的 .length

Javascript 中的 == 运算符不比较两个数组或对象的内容。相反,它只是比较两个数组或对象是否实际上是同一个对象。如果你想看看两者是否包含相同的内容,你可以制作自己的比较功能。对于包含字符串的数组,一种可能的方法是使用 .join():

colors.join("") === initial.join("")

对于所有可能的数组内容,这并非完全万无一失,但如果您知道数组仅包含字符串并且字符串本身不包含逗号,那么这可以作为一种快捷方式。还可以写一个更健壮的内容对比。对于数组的一级深度比较,您可以使用:

function compareArrays(a, b) {
    if (typeof a.length === "number" && a.length === b.length) {
        for (var i = 0; i < a.length; i++) {
            if (a[i] !== b[i]) {
                return false;
            }
        }
        return true;
    }
    return false;
}

对于每个数组都可以包含其他数组或对象的深度比较,您也想比较它们的内容,那么您需要一个可以深度比较对象属性和数组元素的递归解决方案。

您可以像这样对对象和数组进行深入比较:

function compareObjects(a, b) {
    // if both are objects, then do a deep comparison of all properties
    if (typeof a === "object" && typeof b === "object") {
        var aKeys = Object.keys(a).sort(), bKeys = Object.keys(b).sort();
        var prop;
        if (!compareArrays(aKeys, bKeys)) {
            return false;
        }
        // here we know the two objects have the same keys, check values now
        for (var i = 0; i < aKeys.length; i++) {
            prop = aKeys[i];
            if (typeof a[prop] === "object" && typeof b[prop] === "object") {
                if (!compareObjects(a[prop], b[prop])) {
                    return false;
                }
            } else if (a[prop] !== b[prop]) {
                return false;
            }
        }
        return true;
    } else {
        // not both objects so just do straight equality
        return a === b;
    }
}

function compareArrays(a, b) {
    if (typeof a.length === "number" && a.length === b.length) {
        for (var i = 0; i < a.length; i++) {
            if (a[i] !== b[i]) {
                return false;
            }
        }
        return true;
    }
    return false;
}

这将允许您比较其中包含对象的嵌套结构,并比较除数组之外的普通对象,如下所示:

var a = {foo: [1,2,3,["a","b","c"]], fee: {a: "1"}};
var b = {fee: {a: "1"}, foo: [1,2,3,4,["a","b","c"]]};

注意:这是比较对象或数组的公开可枚举属性。它不比较自定义形成的对象(比如创建闭包的构造函数)的私有(例如闭包)变量。对于那些,比较它们的唯一方法是使用自定义比较方法,该方法可以访问对象本身的私有数据。

注意:这不适用于包含循环引用的对象或数组(DOM 对象因循环引用而臭名昭著)。可以通过跟踪所有已经在数组或 Set 中进行比较的引用来使其与循环引用一起使用,并且如果已经在比较它们的过程中则不再递归到它们中。

这就是 javascript 等于 (==) 运算符的工作方式:

这里是严格相等的工作原理 (===):

图片来自JavaScript Equality Table

JavaScript 中我们有 [] === []; // false 因为所有 Objects 都通过引用测试是否相等,你可以在 here (7) and here (1.f).

上查看规范

您可以使用 Array.prototype.every

编写一个函数来执行您想要的测试
function equivArray(a, b) {
    return a.length === b.length && a.every(function (e, i) {return e === b[i];});
}

现在

var foo = ['a', 'b', 1, 2],
    bar = ['a', 'b', 1, 2];

equivArray(foo, bar); // true