ForEach в массиве undefined, созданный конструктором Array

Мне просто интересно, почему невозможно сделать forEach в массиве undefined.

код:

var arr = new Array(5); // [undefined x 5]

//ES5 forEach
arr.forEach(function(elem, index, array) {
    console.log(index);
});

//underscore each
_.each(arr, function(elem, index, array) {
    console.log(index);
});

Оба примера не выполняют функцию.

Теперь, чтобы сделать foreach, я должен сделать:

var arr = [0,0,0,0,0];

Затем сделайте для него Each на нем.

Я пытаюсь создать массив с указанным размером и пропустить его, избегая цикла for. Я думаю, что это более ясное использование forEach, чем для цикла. С массивом длиной 5 это не проблема, но это было бы уродливо с большими массивами.

Почему возникает проблема, связанная с массивом значений undefined?

Ответ 1

Array(5) существенно эквивалентно

var arr = []; 
arr.length = 5;

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

Object.keys(arr)

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

Вы можете проверить это, выполнив:

var arr = Array(5)

//prints nothing
arr.forEach(function(elem, index, array) {
    console.log(index);
});

arr[3] = 'hey'
//prints only 'hey' even though arr.length === 5
arr.forEach(function(elem, index, array) {
    console.log(index);
});

Следующий код:

var arr = [undefined, undefined];

создает и массив из length ===2 и устанавливает оба числовых свойства 0 и 1 в undefined.

Ответ 2

Возможно, поможет упрощенная реализация .forEach().

Array.prototype.my_for_each = function(callback, thisArg) {
    for (var i = 0; i < this.length; i++) {
        if (i in this) {
            callback.call(thisArg, this[i], i, this);
        }
    }
};

Итак, вы можете видеть, что происходит то, что метод выполняет итерацию всего массива (согласно спецификации), но он вызывает только обратный вызов, если элемент действительно существует. Он проверяет, ищет ли свойство (индекс) с помощью оператора in, который проверяет, имеет ли объект свойство или наследует его свойство.

Если in показывает, что индекс существует, обратный вызов вызывается.


Итак, задайте этот массив:

var arr = ["foo", "bar", "baz"];

Это выведет все 3 элемента:

arr.my_for_each(function(item) {
    console.log(item);
});
// "foo" "bar" "baz"

Но если мы используем delete для удаления члена, он оставляет дыру в массиве, который теперь будет передан.

delete arr[1];

arr.my_for_each(function(item) {
    console.log(item);
});
// "foo" "baz"

Когда вы создали массив с использованием Array(5), он создал один без каких-либо элементов, но с .length установлен на 5. Итак, это пример редкого массива (очень редкого в этом случае). Поскольку ни один из индексов не будет найден с помощью in, обратный вызов никогда не будет вызываться.