Длина массива и 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>");
Ответы
Ответ 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.