Да, технически индексы массива — это строки, но, как элегантно выразился Фланаган в своем «Полном руководстве»: полезно четко отличать индекс массива от имени свойства объекта. Все индексы являются именами свойств, но только имена свойств, которые являются целыми числами от 0 до 232-1, являются индексами.
Обычно вам не следует заботиться о том, что браузер (или, в более общем смысле, «скрипт-хост») делает внутри, пока результат соответствует предсказуемому и (обычно/надеюсь) указанному результату. Фактически, в случае JavaScript (или ECMAScript 262) описывается только то, какие концептуальные шаги необходимы. Это (намеренно) оставляет место для скриптового хоста (и браузеров), чтобы придумать умный, меньший и более быстрый способ реализовать указанное поведение.
На самом деле, современные браузеры используют ряд различных алгоритмов для разных типов массивов внутри: важно, что они содержат, насколько они велики, упорядочены ли они, фиксированы ли и оптимизируются ли они во время (JIT) компиляции или они редкие или плотные (да, часто бывает выгодно делать new Array(length_val)
вместо ниндзя []
).
В вашей концепции мышления (при изучении JavaScript) может помочь знание того, что массивы — это просто особый вид объектов. Но они не всегда совпадают с ожидаемыми, например:
var a=[];
a['4294967295']="I'm not the only one..";
a['4294967296']="Yes you are..";
alert(a); // === I'm not the only one..
хотя для неосведомленного программиста легко и довольно прозрачно иметь массив (с индексами) и прикреплять свойства к объекту-массиву.
Лучший ответ (я думаю) взят из спецификации (15.4) себя:
Объекты массива
Объекты-массивы придают особое значение определенному классу имен свойств. Имя свойства P (в виде строкового значения) является индексом массива тогда и только тогда, когда ToString(ToUint32(P)) равно P, а ToUint32(P) не равно 232< /sup>−1. Свойство, имя свойства которого является индексом массива, также называется элементом. Каждый объект Array имеет свойство длины, значение которого всегда является целым неотрицательным числом меньше 232. Значение свойства length численно больше, чем имя каждого свойства, имя которого является индексом массива; всякий раз, когда свойство объекта Array создается или изменяется, другие свойства настраиваются по мере необходимости, чтобы сохранить этот инвариант. В частности, всякий раз, когда добавляется свойство, имя которого является индексом массива, свойство длины при необходимости изменяется, чтобы быть на единицу больше, чем числовое значение этого индекса массива; и всякий раз, когда изменяется свойство длины, каждое свойство, имя которого является индексом массива, значение которого не меньше новой длины, автоматически удаляется. Это ограничение применяется только к собственным свойствам объекта Array и не зависит от свойств длины или индекса массива, которые могут быть унаследованы от его прототипов.
Объект O называется разреженным, если следующий алгоритм возвращает значение true:
Пусть len будет результатом вызова внутреннего метода [[Get]] O с длиной аргумента.
Для каждого целого числа i в диапазоне 0≤i‹ToUint32(len)
а. Пусть elem будет результатом вызова внутреннего метода [[GetOwnProperty]] O с аргументом ToString(i). б. Если элемент не определен, вернуть true.
Вернуть ложь.
Фактически спецификация ECMAScript 262 просто гарантирует программисту JavaScript однозначные ссылки на массивы независимо от получения/установки arr['42']
или arr[42]
вплоть до 32-битного числа без знака.
Основное отличие состоит, например, в (автоматическом обновлении) array.length
, array.push
и других сахарных массивов, таких как array.concat
и т. д. Хотя, да, JavaScript также позволяет зацикливаться на свойствах, установленных для объекта, мы не можем прочитать, сколько мы поставили (без петли). И да, насколько мне известно, современные браузеры (особенно Chrome в том, что они называют (но не указывают точно)) «маленькими целыми числами» очень быстро работают с настоящими (предварительно инициализированными) массивами малых целых чисел.
Также см., например, этот связанный вопрос.
Редактировать: согласно тесту @Felix Kling (из его комментария выше):
После arr[4294967294] = 42;
arr.length
правильно показывает 4294967295
. Однако вызов arr.push(21)
; бросает RangeError: Invalid array length
. arr[arr.length] = 21
работает, но не меняет длину.
Объяснение этого (предсказуемого и предполагаемого) поведения должно быть ясным после этого ответа.
Изменить2:
Теперь кто-то дал комментарий:
for (var i in a) console.log(typeof i) показывает «строку» для всех индексов.
Поскольку for in
— это (неупорядоченный, который я должен добавить) итератор свойств в JavaScript, очевидно, что он возвращает строку (мне было бы чертовски чертовски, если бы это было не так).
Из MDN:
for..in не следует использовать для перебора массива, где важен порядок индексов.
Индексы массива — это просто перечисляемые свойства с целочисленными именами, в остальном они идентичны общим свойствам объекта. Нет никакой гарантии, что for...in вернет индексы в любом конкретном порядке и вернет все перечисляемые свойства, включая свойства с нецелочисленными именами и унаследованные.
Поскольку порядок итерации зависит от реализации, итерация по массиву может не посещать элементы в согласованном порядке. Поэтому лучше использовать цикл for с числовым индексом (или Array.forEach, или цикл for...of) при переборе массивов, где важен порядок доступа.
Итак.. что мы узнали? Если порядок важен для нас (часто с массивами), то нам нужен этот причудливый массив в JavaScript, а наличие «длины» весьма полезно для циклов в числовом порядке.
Теперь подумайте об альтернативе: дайте своим объектам идентификатор/порядок, но тогда вам нужно будет снова перебирать свои объекты для каждого следующего идентификатора/порядка (свойства)...
Редактировать 3:
Кто-то ответил в духе:
var a = ['a','b','c'];
a['4'] = 'e';
a[3] = 'd';
alert(a); // returns a,b,c,d,e
Теперь, используя объяснение в моем ответе: произошло то, что '4'
можно привести к целому числу 4
, и оно находится в диапазоне [0, 4294967295]
, что делает его допустимым массивом index
, также называемым element
. Поскольку var a
является массивом ([]
), массив элемент 4 добавляется как элемент массива, а не как свойство (что произошло бы, если бы var a
был объектом ({}
).
Пример для дальнейшего описания разницы между массивом и объектом:
var a = ['a','b','c'];
a['prop']='d';
alert(a);
посмотрите, как он возвращает a,b,c
без 'd'.
Редактировать 4:
Вы прокомментировали: В этом случае целочисленный индекс следует обрабатывать как строку, поскольку он является свойством массива, который является особым типом объекта JavaScript. Это неверно em> с точки зрения терминологии, потому что: (строки, представляющие) целочисленные индексы (между [0, 4294967295]) создают массив indexes
или elements
; не properties
.
Лучше сказать: как фактическое целое число, и string
, представляющее целое число (оба между [0, 4294967295]), являются действительным массивом index (и концептуально должны рассматриваться как целое число) и создает/изменяет массив элементов (например, "вещи"/значения (только), которые возвращаются, когда вы выполняете arr.join()
или arr.concat()
).
Все остальное создает/изменяет свойство (и концептуально должно рассматриваться как строка). То, что на самом деле делает браузер, обычно не должно вас интересовать, учитывая, что чем проще и понятнее указанный вами код, тем больше шансов, что браузер распознает: «о, давайте оптимизируем это до реального массива под капотом».
person
GitaarLAB
schedule
18.12.2014