为什么不使用等式检查数组

时间:2015-06-13 15:53:12

标签: javascript

我开始时:

"1:2".split(':') == ["1","2"]; 
// false

然后尝试了:

[1,2] == [1,2];
// false

最终:

[] == []; 
// false

我发现了:

"1:2".split(':').toString() == [1,2].toString();
// true

所以我已经解决了我的初始问题(种类),但为什么数组不能相互匹配?

8 个答案:

答案 0 :(得分:31)

Javascript数组是对象,您不能简单地使用等于运算符footer-div来理解这些对象的内容是否相同。等于运算符仅测试两个对象实际上是否完全相同(例如==,适用于myObjVariable==myObjVariablenull)。

如果你需要检查两个数组是否相等,我建议只遍历两个数组并验证所有元素是否具有相同的值(并且两个数组的长度相同)。

关于自定义对象的相同性,我构建了一个特定的undefined函数,并将其添加到您的类的原型中。

考虑到最后你将两个数组转换为equals并测试了结果字符串的相等性,你可能有一天会考虑使用一种类似但更通用的技术,你会发现它不仅仅是几个地方:

String

嗯,

虽然如果属性的顺序对于那些对象实例总是相同的,这可能会起作用,但是这样就可以打开那些难以追踪的非常讨厌的错误。总是喜欢更明确的方法,只需编写一个干净且可读的函数来测试相等性检查所有必需的字段。

答案 1 :(得分:10)

Javascript中的Objects的==运算符仅检查对象是否是相同的实际对象引用,而不是它们是包含相同内容的两个单独对象。没有内置运算符来检查它们是否包含相同的内容。您必须自己编写一个函数来进行这种比较。

只要数组元素只包含原始值(而不是其他对象),您的字符串转换就是比较两个数组的一种方法。如果数组元素可以包含其他元素,那么您必须确保这些对象本身也被转换为代表性字符串。

并且,转换为字符串不会在包含"4"的数组元素与包含4的数组元素之间辨别,因为它们都会在字符串表示形式中转换为"4"

答案 2 :(得分:6)

数组是JavaScript中的对象,通过引用传递。这意味着当我初始化一个数组时:

var array = [1, 2, 3];

我在内存中创建了对该数组的引用。如果我然后说:

var otherArray = [1 2, 3];

array和otherArray在内存中有两个独立的地址,因此它们不会相互相等(即使值相等。)要测试通过引用传递,您可以通过执行以下操作来使用数组:

var copyArray = array;

在这种情况下,copyArray在内存中引用与数组相同的地址,所以:

copyArray === array; //will evaluate to true
array.push(4); //copyArray will now have values [1, 2, 3, 4];
copyArray.push(5); //array will have values [1, 2, 3, 4, 5];
copyArray === array; //still true

为了测试数组或对象中值的相等性,你需要进行“深度比较” - 对于数组,这将遍历两个数组以比较索引和值以查看两者是否相等 - 对于它将遍历每个对象的对象键并确保值相同。有关深度比较的更多信息,请查看underscore.js带注释的源:

http://underscorejs.org/docs/underscore.html

答案 3 :(得分:2)

在javascript中,每个[]都是window.Array类的实例。所以你基本上是想比较两个不同的对象。因为数组可以没有任何数据。任何类型的元素,包括对象和自定义对象,那些嵌套的数组可以再次拥有许多属性和数组,等等。

在比较时变得模棱两可,你永远不会确定你想用这些对象和嵌套属性做什么。因此,您通过比较尝试实现的目标可以通过许多其他方式完成。你必须找到适合你案件的正确方法。

答案 4 :(得分:1)

一种方法是制作自己的Array检查功能:

How to compare arrays in JavaScript?

另一种方法是使用ArrayString转换为.join(),并比较字符串。然后使用Array将它们转换回.split()

答案 5 :(得分:0)

对象的平等性将告诉您这两个对象是否是同一个

var a = [];
var b = a;
a === b;    // True, a and b refer to the same object
[] === [];  // False, two separate objects

您必须遍历数组以查看它们是否具有相同的元素。

请参阅:How to check if two arrays are equal with JavaScript?

答案 6 :(得分:0)

如果我将此问题与Python相关联:

输入:

a="1 2 3 4"

案例I:

a=input.split(' ')

输出:['1','2','3','4']

情况二:

a=map(int,input.split(' '))

输出:[1、2、3、4]

所以,错误是类型的错误,因为它可能对以下情况显示为“ true”:

"1:2".split(':').toString() == [1,2].toString(); //true

答案 7 :(得分:-1)

我的猜测是因为当设计javascript时,他们认为元素明智的比较是一个很少使用的功能,因此它从未被放入语言中。

此功能在流行语言中相当罕见; Java不支持它,C#不支持它,C ++ stl类型不支持它。

与参考比较相比,元素智能比较相当昂贵且复杂。

在一个完美的世界中,所有东西都可以通过引用进行比较,这样每个具有相同状态的两个对象都会有相同的引用,只需将它们的内部虚拟地址与简单的内容进行比较,就可以非常便宜地检查相等性数字比较。

不幸的是,我们并没有生活在一个完美的世界中,上述内容只适用于带字符串池的字符串,以及其他一些相对内存昂贵的缓存选项。

Prolog和Haskell等一些语言允许按价值进行比较;例如

myProgram :- 
    Object1 = [1, "something", true, [1,[[], []], true,[false]]],
    Object2 = [1, "something", false, [1,[[], []], true,[false]]],
    Object3 = [1, "something", true, [1,[[], []], true,[false]]],
    Object4 = [1, "something", false, [1,[[], []], true,[false]]],
    (Object1 = Object2 
        -> write("first test passed.")    
        ; write("first test failed\n")),
    (Object1 = Object3 
        -> write("second test passed!\n")
        ; write("second test failed!\n")),
    (Object2 = Object4
        -> write("third test passed!\n")
        ; write("third test failed!")).

您可以使用从构造函数到该构造函数的比较器的映射,以任何语言实现您自己的深度比较器。由于JavaScript没有除了字符串到对象之外的任何地图,并且javascript客户端无法访问对象的内部唯一标识符,因此我们需要使用带有构造函数,比较器对的表,如下所示。

class MyList {
    constructor(a, b) {
        this.head_ = a;
        this.tail_ = b;
    }
    getHead() {
        return this.head_;
    }
    getTail() {
        return this.tail_;
    }
    setHead(x) {
        this.head_ = x;
    }
    setTail(x) {
        this.tail_ = x;
    }
    equals(other) {
        if (typeof other !== 'object') {
            console.log(other, 'EEP');
            return false;
        }
        if (!(other instanceof MyList)) {
            console.log(other, 'EEP');
            return false;
        }

        var h = this.head_;
        var ho = other.getHead();
        var cmp1 = comparatorof(h);
        if (!cmp1(h, ho)) {
            return false;
        }
        var t = this.tail_;
        var to = other.getTail();
        var cmp2 = comparatorof(t);
        if (!cmp2(t, to)) {
            return false;
        }

        return true;
    }
}

var object1 = {
    0: "one",
    1: "two",
    2: ["one", "two", []],
    something: {
        1: [false, true]
    }
};

function strictComparator(a, b) {
    return a === b;
}

// given any object, tries finding a function for comparing 
// that object to objects of the same kind. Kind being
// used loosely, since some objects like null resist being categorized,
// so these objects need special alteration of the comparatorof itself.
function comparatorof(x) {
    if (x === null || x === undefined) {
        return strictComparator;
    }

    x = Object(x); // promote primitives to objects
    var ctor = x.constructor;
    var c2ct = ctorToComparatorTable;
    var n = c2ct.length;

    for (var i = 0; i < n; ++i) {
        var record = c2ct[i];
        var keyCtor = record[0];

        if (keyCtor === ctor) {
            return record[1];
        }
    }
    throw new TypeError('no comparator exists for ' + x);
}

var ctorToComparatorTable = [
    [String, function(a, b) {
        return a == b;
    }],
    [Object, function(a, b) {
        for (var key in a) {
            var avalue = a[key];
            var bvalue = b[key];
            var cmp = comparatorof(avalue);

            if (!cmp(avalue, bvalue)) {
                return false;
            }
        }

        return true;
    }],
    [Number, function(a, b) {
        return a == b;
    }],
    [Boolean, function(a, b) {
        return a == b;
    }],
    [Array, function(as, bs) {
        if (typeof bs !== 'object') {
            return false;
        }
        var nAs = as.length;
        var nBs = bs.length;

        if (nAs !== nBs) {
            return false;
        }

        for (var i = 0; i < nAs; ++i) {
            var a = as[i];
            var b = bs[i];
            var cmp = comparatorof(a);

            if (!cmp(a, b)) {
                return false;
            }
        }

        return true;
    }],
    [MyList, function(a, b) {
        return a.equals(b);
    }]
];

// true
console.log(comparatorof([])([new MyList(1, new MyList(2, 3))], [new MyList(1, new MyList(2, 3))]));
// true
console.log(comparatorof([])([{}, new MyList(1, new MyList(2, 3))], [{}, new MyList(1, new MyList(2, 3))]));
// false
console.log(comparatorof([])([{}, new MyList(1, new MyList(2, 3))], [{}, new MyList(1, new MyList(3, 3))]));
// true
console.log(comparatorof({})({
    1: 'one',
    one: '1',
    x: new MyList(1, {})
}, {
    1: 'one',
    one: '1',
    x: new MyList(1, {})
}));
// true
console.log(comparatorof(2)(
    3,
    3
));
//true
console.log(comparatorof(true)(
    true,
    true
));
//false
console.log(comparatorof([])(
    [1, 2, new MyList(1, 2)], [1, 2, new MyList(4, 9)]
));
// true
console.log(comparatorof([])(
    [1, 2, new MyList(1, 2), null], [1, 2, new MyList(1, 2), null]
));
// false
console.log(comparatorof(null)(
    null,
    undefined
));
// true
console.log(comparatorof(undefined)(
    undefined,
    undefined
));
// true
console.log(comparatorof(null)(
    null,
    null
));

一个很大的问题是,ES6已经充满了与JScript和Nashorn JJS以及ActionScript不兼容的功能,这种语言可能每隔几个月就会被重新命名为一种全新的语言,考虑到这是有效的。您将新功能添加到一种语言中,该语言会破坏与旧版本的兼容性,如果没有额外的评估图层则无法复制。

这个问题可以追溯到很长一段时间,你有一个像Lisp这样的最小语言,它“更容易”(仍然不能超载'运算符)引入“一流”功能而不破坏向后兼容性,然后你拥有像Perl这样的语言,如果没有额外的eval层,就无法使用新的一流关键字进行扩展。 JavaScript选择了第二种方法,并且通常通过使用Math,Object等辅助对象来引入新功能来绕过后果,但是只要它想要添加“第一类构造”,它最终会破坏向后兼容性。

作为一个概念证明,在In Lisp中你可以重载==运算符,而在JavaScript中你不能,而在Perl中你可以通过一种保留关键字的机制。