Разница между (i в массиве) и для (var я = 0; я <array.length; я ++)

Это будет очень простой вопрос для всех вас, javascript/jquery.

Я программировал javascript за последние 2 года, и сегодня у меня возникла странная проблема.

Я извлекаю JSONarray из моего C# Webmethod и называя его jQuery.ajax()

Когда я делаю

for (i in JSONRaw) {
    Index = Index + 1;
    $('#bottomGridDashboard').append('<tr> <td>' + Index + '</td> <td>' + JSONRaw[i].DisplayName + '</td> <td>' + JSONRaw[i].SpeedInKPH + '</td> <td><img src="' + JSONRaw[i].ImageURL + '" height="20px" alt="" /></td> <td>' + JSONRaw[i].DepotID + '</td> <td>' + JSONRaw[i].RouteID + '/' + JSONRaw[i].ScheduleID + '/' + JSONRaw[i].TripID + '</td> <td>' + JSONRaw[i].Direction + '</td> <td>' + JSONRaw[i].LastStop + '</td> <td> ' + JSONRaw[i].ETAMinutes + ' </td> </tr>');
}

добавленная таблица добавляет две дополнительные строки, которые помещают каждое поле как "undefined". Посмотрите это изображение

Однако, если я заменил цикл на

for (var i = 0; i < JSONRaw.length;i++ ) {
    Index = Index + 1;
    $('#bottomGridDashboard').append('<tr> <td>' + Index + '</td> <td>' + JSONRaw[i].DisplayName + '</td> <td>' + JSONRaw[i].SpeedInKPH + '</td> <td><img src="' + JSONRaw[i].ImageURL + '" height="20px" alt="" /></td> <td>' + JSONRaw[i].DepotID + '</td> <td>' + JSONRaw[i].RouteID + '/' + JSONRaw[i].ScheduleID + '/' + JSONRaw[i].TripID + '</td> <td>' + JSONRaw[i].Direction + '</td> <td>' + JSONRaw[i].LastStop + '</td> <td> ' + JSONRaw[i].ETAMinutes + ' </td> </tr>');
}

строки undefined исчезают.. Смотрите изображение

Я извиняюсь за свою глупость, но раньше я никогда не сталкивался с такой проблемой. В чем может быть причина?

EDIT:

Массив отлично. И в нем нет дыр. Другое замечание состоит в том, что свойства undefined появляются только в нижней части моей сетки. Я думаю, что он обрабатывает два дополнительных индекса больше, чем длина массива.

РЕДАКТИРОВАТЬ-2

Мой console.log показал мне следующие элементы в моем массиве.

http://i.imgur.com/rI5TjdK.jpg

Я объявил прототипы на моей главной странице.

Array.prototype.inArray = function (comparer) {
    for (var i = 0; i < this.length; i++) {
        if (comparer(this[i])) return true;
    }
    return false;
};


Array.prototype.pushIfNotExist = function (element, comparer) {
    if (!this.inArray(comparer)) {
        this.unshift(element);
    }
};

Увеличивает ли длина массива.?

Ответ 1

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

(1) Первый должен быть for (var i in JSONRaw). Установка свойства i на window может иметь неочевидные последствия.

(2) JSONRaw не является массивом, а объектом, который имеет плохо определенное свойство length.

(3) На JSONRaw появились другие свойства, которые появляются в первом цикле.

(4) Массив может иметь "дыры". Например, массив ниже имеет отверстие с индексом 1.

var a = [];
a[0] = 0;
a[2] = 2;

Первый способ опустить 1. Второй способ будет включать индекс 1 (при a[1] будет undefined).

Аналогичная ситуация может произойти следующим образом:

var a = [0, 1];
a.length = 3;

Лично я подозреваю (3).

Выполнение console.log в первом цикле может выявить проблему.

EDIT: Из вашего отладочного вывода кажется, что (3) был виновником. inArray и pushIfNotExist являются ключами на всех массивах, поэтому первый цикл for повторяется над ними.

Если вы собираетесь использовать первый цикл, у вас есть три варианта:

(1) Не добавляйте эти функции на Array.prototype. Добавление к встроенным прототипам обычно обескураживается.

(2) Используйте Object.defineProperty (не будет работать в IE8):

Object.defineProperty(Array.prototype, 'inArray', {
    enumerable: false,   //this makes it not show up in the for loop
    value: function (comparer) {
        for (var i = 0; i < this.length; i++) {
            if (comparer(this[i])) return true;
        }
        return false;
    }
});

(3) Проверьте, был ли ключ определен на прототипе.

for(var i in JSONRaw) {
    if(JSONRaw.hasOwnProperty(i)) {
        //do something
    }
}

Хотя большинство людей просто используют второй цикл из-за потенциальных ошибок (и более быстрой работы).

Ответ 2

JavaScript for-in и С# foreach - совсем другие вещи. Не используйте for-in для циклического перемещения массивов, если вы не используете соответствующие меры предосторожности, а не то, для чего это необходимо. Он перебирает перечислимые свойства объектов, а не индексы или записи массива.

Используйте либо необходимые меры предосторожности, либо используйте общий for, как ваш второй пример, или (если вы можете положиться на его использование) используйте Array#forEach.

Подробнее в this SO answer и в моем блоге.


Отдельно: убедитесь, что вы объявляете i где-то (вы, кажется, не в первом примере). Если вы этого не сделаете, вы становитесь жертвой Ужас неявных глобалов.


Я объявил прототипы на моей главной странице.

Array.prototype.inArray = function ...

Вот почему вы не используете for-in для циклического перемещения массивов без каких-либо гарантий. Опять же, см. Ссылки выше. Ваш цикл for-in будет проходить через свойства индекса массива ("0", "1" и т.д. - да, это действительно строки), а также имена функций, которые вы добавили в Array.prototype (inArray и т.д.).

Ответ 3

В нескольких словах цикл for ... in выполняет итерацию через keys из object, а цикл c-like for выполняет итерацию через индексы массива. JS не так сильно набирается, поэтому он позволяет вам получить оба пути, НО правильные способы для массива - это только второй или "метод forEach". Вы можете прочитать больше в MDN.