Подтвердить что ты не робот

Почему проверка равенства не выполняется с массивами

Я начал с:

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

Затем попытался:

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

и в конечном итоге:

[] == []; 
// false

С тех пор я обнаружил, что:

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

Итак, я решил свою первоначальную проблему (вроде), но почему массивы не могут совпадать?

4b9b3361

Ответ 1

Массивы Javascript - это объекты, и вы не можете просто использовать оператор равенства ==, чтобы понять, является ли содержимое этих объектов одинаковым. Оператор равенства будет проверять только если два объекта на самом деле являются точно одним и тем же экземпляром (например, myObjVariable==myObjVariable, работает и для null и undefined).

Если вам нужно проверить, равны ли два массива, я бы рекомендовал просто пересекать оба массива и убедиться, что все элементы имеют одинаковое значение (и что два массива имеют одинаковую длину).

Что касается равенства пользовательских объектов, я бы вместо этого создал определенную функцию equals, и я добавлю ее в прототип вашего класса.

Учитывая, что в конце вы преобразовали оба массива в String и проверили равенство полученных строк, вы могли бы однажды использовать аналогичный, но более общий метод, который вы найдете в более чем нескольких местах:

JSON.stringify(OBJ1) === JSON.stringify(OBJ2) 

Ну, не.

Хотя это может работать, если порядок свойств всегда будет одинаковым для этих экземпляров объекта, это оставляет дверь открытой для чрезвычайно неприятных ошибок, которые трудно отследить. Всегда предпочитайте более явный подход и просто пишите чистую и читаемую функцию, которая будет проверять равенство, проверяя все необходимые поля.

Ответ 2

Оператор == для объектов в Javascript проверяет, являются ли объекты одной и той же ссылкой на фактический объект, а не двумя отдельными объектами, которые содержат одно и то же содержимое. Нет встроенного оператора для проверки того, содержат ли они одно и то же содержимое. Вам нужно будет написать функцию, чтобы выполнить подобное сравнение.

Преобразование строк является одним из способов сравнения двух массивов, если элементы массива содержат только примитивные значения (а не другие объекты). Если элементы массива могут содержать другие элементы, вам нужно будет убедиться, что эти объекты сами были преобразованы в репрезентативные строки.

И преобразование в строку не будет различаться между элементом массива, содержащим "4" по сравнению с тем, который содержит 4, поскольку оба преобразуются в "4" в строковое представление.

Ответ 3

Массивы - это объекты в 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

Ответ 4

В javascript каждый [] является экземпляром класса window.Array. Таким образом, вы в основном пытаетесь сравнить два разных объекта. Поскольку у массива может быть no. и любые типы элементов, включая объекты и пользовательские объекты, и те вложенные массивы могут снова иметь множество свойств и массивов и т.д.

Это становится неоднозначным, когда дело доходит до сравнения, вы никогда не будете уверены, что вы хотите делать с этими объектами и вложенными свойствами. То, чего вы пытаетесь достичь путем сравнения, может быть сделано многими другими способами. Вам просто нужно выяснить правильный путь для вашего дела.

Ответ 5

Один из способов - сделать свою собственную функцию проверки Array:

Как сравнить массивы в JavaScript?!

Другой способ - преобразовать Array в String с помощью .join() и сравнить строки. Затем преобразуйте их обратно в Array с помощью .split().

Ответ 6

Равенство для объектов скажет вам, являются ли два объекта одним и тем же.

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

Вам придется перебирать массивы, чтобы увидеть, имеют ли они одинаковые элементы.

Смотрите: Как проверить, равны ли два массива с JavaScript?

Ответ 7

Если я связываю эту проблему с этим в Python:

Вход:

a="1 2 3 4"

случай I:

a=input.split(' ')

вывод: ['1', '2', '3', '4']

вариант II:

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

вывод: [1, 2, 3, 4]

Таким образом, ошибка относится к типу, так как он может быть "истинным" для:

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

Ответ 8

Я предполагаю, что, поскольку при разработке javascript они считали, что элементное сравнение было редко используемой функцией, поэтому он никогда не был помещен в язык.

Эта функция довольно распространена на популярных языках; Java не поддерживает его, С# не поддерживает его, 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, что язык может также быть переименован как совершенно новый язык каждые несколько месяцев, учитывая, что эффективно то, что вы получаете, когда добавляете новые функции на язык, который нарушает совместимость со старыми версиями, которые не могут быть реплицированы без дополнительного уровня eval.

Эта проблема длится долгое время, когда с одной стороны у вас есть минимальный язык, такой как Lisp, который "легче" (до сих пор не может перегружать "оператор"), чтобы внедрять функции "первого класса", не нарушая обратной совместимости, а затем у вас есть языки, такие как Perl, который не может быть расширен с помощью новых первоклассных ключевых слов без дополнительного уровня eval. JavaScript выбрал второй подход и, как правило, обходит последствия, используя вспомогательные объекты, такие как Math, Object, чтобы вводить новые функции, но в любое время, когда он хочет добавить "конструкции первого класса", он в конечном итоге нарушает обратную совместимость.

Как доказательство концепции, в Lisp вы можете перегрузить == operator, тогда как в JavaScript вы не можете, а в Perl вы можете, но через механизм, который резервирует ключевые слова.