Является ли индекс массива JavaScript строкой или целым числом?

У меня был общий вопрос о массивах JavaScript. Индексы массивов в JavaScript внутренне обрабатываются как строки? Я где-то читал, что поскольку массивы являются объектами в JavaScript, индекс на самом деле является строкой. Я немного смущен этим и был бы рад любому объяснению.

5 ответов


правильно так:

> var a = ['a','b','c']
undefined
> a
[ 'a', 'b', 'c' ]
> a[0]
'a'
> a['0']
'a'
> a['4'] = 'e'
'e'
> a[3] = 'd'
'd'
> a
[ 'a', 'b', 'c', 'd', 'e' ]

формально все имена свойств являются строками. Это означает, что массивные числовые имена свойств действительно ничем не отличаются от любых других имен свойств.

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

теперь внутри, среда выполнения JavaScript может реализовать функциональность массива любым способом.

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


в JavaScript существует два типа массивов: стандартные массивы и ассоциативные массивы (или объект с свойствами)

  • [ ] - стандартный массив-только целочисленные индексы на основе 0
  • { } - ассоциативный массив-объекты JavaScript, где ключами могут быть любые строки

Так ...

var arr = [ 0, 1, 2, 3 ];

... определяется как стандартный массив, где индексы могут быть только целыми числами. Когда вы делаете arr["что-то"], так как что-то (что вы используете как index) не является целым числом, вы в основном определяете свойство объекта arr (все является объектом в JavaScript). Но вы не добавляете элемент в стандартный набор.


давайте посмотрим:

[1]["0"] === 1 // true

О, но это не окончательно, так как время выполнения может быть принудительным "0" to +"0" и +"0" === 0.

[1][false] === undefined // true

теперь +false === 0, поэтому нет, среда выполнения не принуждает значение к числу.

var arr = [];
arr.false = "foobar";
arr[false] === "foobar" // true

таким образом, на самом деле среда выполнения принуждает значение к строке. Так что да, это поиск хэш-таблицы (внешне).


Да, технически массив-индексы являются строками, но, как изящно выразился Фланаган в своем "окончательном руководстве":
"Полезно четко отличать индекс массива от имени свойства объекта. Все индексы являются именами свойств, но только имена свойств, которые являются целыми числами от 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..

хотя легко и довольно прозрачно для неинформированного программиста иметь массив (с индексами) и присоединить свойства к массиву-объекту.

лучший ответ (я думаю) от spec (15.4):

Объекты Array

объекты массива дают специальное обращение к определенному классу свойств имена. имя свойства P (в виде строкового значения) является массивом индекс тогда и только тогда, когда ToString(ToUint32 (P)) равен P и ToUint32 (P) не равно 232-1. Свойство имя свойства которого индекс массива также называется элементом. Каждый объект Array имеет свойство length, значение которого всегда является неотрицательным целым числом меньше 232. Значение свойства length численно больше, чем имя любого свойства, имя которого является индексом массива; когда свойство объекта массива создается или изменяется, другие свойства корректируются по мере необходимости для поддержания этого инварианта. Конкретно, всякий раз, когда добавляется свойство, имя которого является индексом массива, длина свойство при необходимости изменяется на одно больше числового значение этого индекса массива; и всякий раз, когда свойство length изменилось каждое свойство, имя которого является индексом массива, значение которого не меньше новой длины автоматически удаляется. Этот ограничение применяется только к собственным свойствам объекта Array и не зависит от свойств индекса length или array, которые могут быть унаследованы от его прототипов.

объект, O, считается разреженным, если возвращается следующий алгоритм правда:

  1. пусть len является результатом вызова [[Get]] внутреннего метода O с аргументом "length".
  2. для каждого целого числа i в диапазоне 0≤i

    a. Пусть elem является результатом вызова внутреннего метода [[GetOwnProperty]] O с аргументом ToString(i).
    b. Если elem не определен, верните true.

  3. возвращает false.

эффективно спецификация ECMAScript 262 просто гарантирует javascript-программисту однозначные ссылки на массив независимо от получения/настройки arr['42'] или arr[42] до 32bit без знака.

основное отличие, например, (автоматическое обновление)array.length, array.push и другой массив-сахар, как array.concat etc.
Хотя, да, javascript также позволяет один цикл над свойства, установленные для объекта, мы не можем прочитать, сколько мы установили (без цикла). и да, насколько мне известно, современные браузеры (особенно chrome в том, что они называют (но точно не указывают)) "маленькие целые числа" очень быстро с истинными (предварительно инициализированными) малыми массивами int.

Также см., например,этой соответствующий вопрос.

Edit: согласно тесту @ Felix Kling (из его комментария выше):

после arr[4294967294] = 42;, arr.length правильно показывает 4294967295. Однако, вызывая arr.push(21); бросает!--14-->. arr[arr.length] = 21 работает, но не меняет длину.

объяснение этого (предсказуемого и предполагаемого) поведения должно быть ясным после этого ответа.

Edit2:

теперь, кто-то дал комментарий:

для (var i в A) консоли.log (typeof i) показывает "строку" для всех индексы.

С for in является (неупорядоченным I должны add) итератор свойств в javascript, это вроде очевидно, что он возвращает строку (я был бы довольно проклят, если бы это не так).

С MDN:

for..in не следует использовать для итерации по массиву, где порядок индексов очень важный.

индексы массива - это просто перечисляемые свойства с целочисленными именами и в противном случае идентичны общие свойства объекта. Нет гарантия того, что for...in вернет индексы в любом конкретном order и он вернет все перечисляемые свойства, включая те с нецелыми именами и теми, которые наследуются.

поскольку порядок итерации зависит от реализации, итерация над массивом не может посещать элементы в согласованном порядке. Следовательно лучше использовать цикл for с числовым индексом (или массив.инструкция foreach или для...петли), когда итерации по массивам, где порядок доступ важен.

так.. что мы узнали? Если для нас важен порядок (часто с массивами), то мы нужно этот причудливый массив в javascript и имеющий "длину" довольно полезен для циклирования в числовом порядке.

теперь подумайте об альтернативе: дайте вашим объектам id/order, но тогда вам нужно будет снова перебирать ваши объекты для каждого следующего id/order (property)..

Edit 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. Так как ВАР a массив ([]), массив элемент 4 добавляется как array элемент, не как свойство (что бы случалось, если var a объект ({}).

пример для дальнейшего описания разницы между массивом и объектом:

var a = ['a','b','c']; 
a['prop']='d'; 
alert(a);

смотрите, как он возвращается a,b,c без "d", чтобы быть замеченным.

Edit 4:

комментарии: " в этом случае целочисленный индекс должен обрабатываться как строка, так как это свойство массива, который является специальным типом объекта JS."
То есть неправильно с точки зрения терминологии, потому что: (строки, представляющие) целочисленные индексы (между [0, 4294967295]) создают массив indexes или elements; не properties.

лучше сказать: как фактическое число и a string представление целого числа (оба между [0, 4294967295]) является допустимым массивом индекс (и концептуально следует рассматривать как целое число) и создает/изменяет массив элементов ("вещи" /значения (только) что получить обратно, когда вы делаете arr.join() или arr.concat() например).
Все остальное создает/изменяет свойства (и концептуально следует рассматривать как строку).
То, что браузер действительно делает, обычно не должно вас интересовать, отмечая, что чем проще и понятнее указанный вами код, тем больше шансов, что браузер должен распознать: "О, давайте оптимизируем это до фактического массива под капотом".