Длина массива и неопределенные индексы

17

Я просто хочу понять, как работают массивы 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>");
    
задан Ahmet Can Güven 06.07.2015 в 00:27
источник
  • Свойство length всегда по крайней мере на один больше, чем самый большой индекс. Это самонастраивается. –  RobG 06.07.2015 в 00:41
  • Важное примечание: не используйте document.write, он не делает то, что вы думаете. Например, он не выполняет «печать некоторых данных». Это чрезвычайно низкоуровневая функция, и у современных JS есть тонны лучших способов сделать то, что в 1998 году нам понадобилось document.write для. –  Mike 'Pomax' Kamermans 06.07.2015 в 00:41
  • @ Mike'Pomax'Kamermans, да, я не буду в следующий раз. Все, спасибо за все ответы. –  Ahmet Can Güven 06.07.2015 в 00:43
  • @ Mike'Pomax'Kamermans См. также stackoverflow.com/a/802894 –  guest271314 06.07.2015 в 00:55
  • Возможный дубликат stackoverflow.com/questions/22448330/... –  Salman A 06.07.2015 в 09:45
Показать остальные комментарии

6 ответов

11

Примечание. Индексы массива - это ничего, кроме свойств объектов 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 .

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

ответ дан thefourtheye 06.07.2015 в 00:37
источник
8

Существует разница между свойством 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 , тем временем, только что настроено так, чтобы быть больше, чем любой индекс, который вы назначаете, если этот индекс больше или равен текущей длине.

    
ответ дан Ry- 06.07.2015 в 00:33
источник
  • "длина, между тем, всегда всегда больше, чем самое высокое свойство, которое является индексом". +1 –  christopher 06.07.2015 в 00:39
  • @ Кристофер - «... всегда есть хотя бы еще один ...». –  RobG 06.07.2015 в 00:53
3

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

var arr = [];
arr[5] = "a thing";
arr[2] = undefined;
arr = arr.filter(Boolean);
document.write(arr.length);
    
ответ дан guest271314 06.07.2015 в 00:39
источник
3

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

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] выводится как таковой.

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

    
ответ дан Orlando Paredes Hamsho 06.07.2015 в 00:40
источник
  • Это просто неверно для JS. JS-массив - это просто объект JS (тот, который вы знаете как {}), но с некоторыми специальными функциями итерации и специальная обработка ключа-valye. Установка arr = [], а затем arr [5] = «something» НЕ создает что-то на «шестой позиции», оно просто связывает свойство «что-то» с ключом объекта 5. Длина массива будет сообщать 6, но массив сам будет содержать 1 элемент. –  Mike 'Pomax' Kamermans 06.07.2015 в 00:42
  • «пустые позиции» в литерале массива являются исключениями и представлены запятыми: [,,,,, «вещь»]. Говорят, что такие члены исключены, они не существуют и возвращаются без указания при доступе к чтению. –  RobG 06.07.2015 в 00:43
  • @ Mike'Pomax'Kamermans не может не согласиться с тем, что вы говорите, в том смысле, что он семантически корректен для javascript. Вы правы, у массивов есть ключи и значения, а не позиции. Я отредактирую свой ответ. Кроме того, RobG я пытался объяснить концепцию самым простым способом, также понял, подчеркивает, где проще использовать в качестве представления. В любом случае я не могу не согласиться с вами в семантике. –  Orlando Paredes Hamsho 06.07.2015 в 00:45
2

Массивы 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] игнорируется.     

ответ дан Giuseppe Pes 06.07.2015 в 00:39
источник
0

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

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

    
ответ дан Catalyst 06.07.2015 в 00:33
источник
  • @RobG достаточно справедливо. Я отредактирую ответ. –  Catalyst 06.07.2015 в 01:12