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

Длина массива и undefined индексы

Я просто хочу понять, как работают массивы JavaScript, но у меня есть сложная проблема.

Сначала я создал свой массив:

var arr = [];

И установите в нем несколько элементов:

arr[5] = "a thing";
arr[2] = undefined;

Я думал, что у меня должен быть массив размером 2, потому что у меня есть только два объекта с двумя конкретными индексами. Поэтому я тестировал его с помощью свойства .length массивов:

document.write(arr.length + "<br>");

Результат, интересно, равен 6. Но он должен содержать два элемента. Как его размер может быть 6? Вероятно, это связано с последним индексом, который я использовал здесь arr[5] = "a thing";

Затем я попытался перевернуть его:

var size = 0;
for(var x in arr){
 size++;   
}

И переменная size теперь равна 2. Итак, что я узнал из этого: если я использую цикл for in, я буду вычислять, сколько в нем свойств, а не его последний индекс.

Но если я попытаюсь document.write(arr[4]) (который еще не установлен), он пишет undefined.

Итак, почему arr[2] подсчитывается в цикле for..in, но не arr[4]?

Позвольте мне ответить на мой вопрос: что я думал о typeof undefined == undefined, что удивительно верно. Но это JavaScript, нам нужно играть с ним, используя его собственные правила:)

jsFiddle и фрагмент ниже.

var arr = [];

arr[5] = "a thing";
arr[2] = undefined;
document.write(arr.length + "<br>");

var size = 0;
for(var x in arr){
 size++;   
}

document.write(size + "<br>");

document.write(arr[4] + "<br>");
4b9b3361

Ответ 1

Примечание. Индексы массива - это ничего, кроме свойств объектов Array.

Цитирование MDN Отношения между length и численными свойствами,

При установке свойства в массиве JavaScript, когда свойство является допустимым индексом массива, и этот индекс находится за пределами текущих границ массива, двигатель будет соответствующим образом обновлять свойство массива length.

Цитата ECMA Script 5 Спецификация Объекты массива,

всякий раз, когда добавляется свойство, имя которого является индексом массива, свойство длины изменяется, если необходимо, на одно больше, чем числовое значение этого индекса массива; и всякий раз, когда свойство length изменяется, каждое свойство, имя которого является индексом массива, значение которого не меньше, чем новая длина, автоматически удаляется

Итак, когда вы устанавливаете значение в индексе 5, механизм JavaScript настраивает длину массива на 6.


Цитата ECMA Script 5 Спецификация Объекты массива,

Имя свойства P (в виде значения String) является индексом массива тогда и только тогда, когда ToString(ToUint32(P)) равно P и ToUint32(P) не равно 2 32 -1.

Итак, в вашем случае 2 и 4 являются допустимыми индексами, но в массиве определен только 2. Вы можете подтвердить, что вот так

arr.hasOwnProperty(2)

Другие индексы еще не определены в массиве. Таким образом, ваш объект массива называется разреженным объектом массива.

Итак, почему arr [2] подсчитывается для in..in цикла, а не arr [4] не считается?

for..in перечисляет все допустимые перечислимые свойства объекта. В вашем случае, поскольку только 2 является допустимым свойством в массиве, он будет засчитан.

Но когда вы печатаете arr[4], он печатает undefined, потому что JavaScript вернет undefined, если вы попытаетесь получить доступ к свойству, которое не определено в объекте. Например,

console.log({}['name']);
// undefined

Аналогично, поскольку 4 еще не определен в arr, возвращается undefined.


Пока мы на эту тему, вы также можете прочитать эти ответы,

Ответ 2

Разница между свойством, имеющим значение undefined, и свойство, которое не существует, показано здесь с помощью оператора in:

var obj = {
    one: undefined
};

console.log(obj.one === undefined); // true
console.log(obj.two === undefined); // true

console.log('one' in obj); // true
console.log('two' in obj); // false

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

Наконец, чтобы объяснить поведение, которое вы видите: цикл for in будет перебирать только те ключи, где этот ключ in объект (и перечислим).

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

Ответ 3

Чтобы удалить значения undefined из массива, попробуйте использовать .filter()

var arr = [];
arr[5] = "a thing";
arr[2] = undefined;
arr = arr.filter(Boolean);
document.write(arr.length);

Ответ 4

Все сводится к мысли о том, как пространство обрабатывается машинами. Начнем с простейшей идеи:

var arr =[];

Это, в свою очередь, создает местоположение, где вы можете хранить информацию. Как отметил @Mike "Pomax" Камерманс: Это место является специальным объектом javascript, который, в свою очередь, функционирует как набор ключей и значений, например:

arr[key] = value;

Теперь переходим через ваш код:

arr[5] = "a thing";

Теперь машина понимает, что вы создаете что-то в значении 6-й позиции/ 5-й клавиши (поскольку первая позиция массива равна 0). Итак, вы завершаете что-то похожее на это:

arr[,,,,,"a thing"];

Эти запятые представляют собой пустые позиции (как указано в @RobG) в вашем массиве.

То же самое происходит, когда вы заявляете:

arr[2] = undefined;
arr[,,undefined,,,"a thing"];

Итак, когда вы повторяете внутри массива, используя "для var in", вы проверяете каждое из пространств в этом массиве, которые заполнены, поэтому по очереди 2.

В отличие от того, когда вы проверяете длину массива, вы видите, что сколько пространств для хранения информации существует внутри массива, что в свою очередь составляет 6.

Наконец, javascript интерпретирует пустую комнату в массиве как неопознанные значения, что является причиной, по которой arr [4] выводится как таковой.

Надеюсь, что ответили на ваш вопрос.

Ответ 5

Матрицы JavaScript, по крайней мере в первой версии, были простым объектом с свойством length. Следствием этого является большая часть странного поведения, которое вы испытали.

Результат интересный, это 6. Но он должен содержать два данных, как его размер может быть 6? Вероятно, это связано с последним индексом, что я используется здесь arr [5] = "вещь";

Это приводит к 6, потому что length всегда 1 выше самого высокого индекса, даже если в массиве на самом деле меньше элементов.

o почему arr [2] подсчитывается для in..in loop, а не arr [4] не учитывается?

потому что когда вы делаете:

arr[2] = undefined;

Фактически вы добавляете к объекту массива ключ с именем 2. В результате значение arr[2] подсчитывается в цикле for in, а a[4] игнорируется.

Ответ 6

Назначение задает свойство массива, так что когда вы выполняете стиль for var i in для цикла, вы видите только те свойства, которые были установлены (даже если вы установили их как undefined). Когда вы назначаете новое свойство integery, такое как arr[6], массив изменяет длину массива до 7. Память, лежащая в основе массива, может быть или не быть перераспределена соответствующим образом, но она будет доступна вам, когда вы ее используете - если ваша система не в памяти.

Отредактировано в соответствии с комментарием RobG о том, что говорит ECMA-262.