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

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

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

4b9b3361

Ответ 1

Это верно так:

> 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' ]

Ответ 2

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

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

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

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

Ответ 3

Да, технически массивные индексы являются строками, но, как Фланаган элегантно положил его в свое "Окончательное руководство":
"Полезно четко различать индекс массива от имени свойства объекта. Все индексы - это имена свойств, но только имена свойств, которые являются целыми числами от 0 до 2 32 -1, являются индексами".

Обычно вам все равно, что браузер (или больше вообще script -host ') делает внутренне, пока результат соответствует прогнозируемому и (обычно/надежно) заданному результату. Фактически, в случае javascript (или ECMAScript 262) описывается только с точки зрения того, какие концептуальные шаги необходимы. Это (намеренно) оставляет место для script -host (и браузеров), чтобы придумать умный малый и быстрый способ реализовать указанное поведение.

Фактически, современные браузеры используют внутри себя несколько разных алгоритмов для разных типов массивов: важно, что они содержат, насколько они велики, если они в порядке, если они фиксированы и оптимизированы при компиляции (jit) или если они редки или плотны (да, он часто платит, чтобы сделать new Array(length_val) вместо ninja []).

В вашей концепции мышления (при изучении 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):

Объекты массива

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

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

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

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

  • Возвращает false.

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

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

Также см., например, этот связанный с этим вопрос.

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

После arr[4294967294] = 42;, arr.length правильно показывает 4294967295. Однако вызов arr.push(21); выбрасывает a RangeError: Invalid array length. arr[arr.length] = 21 работает, но не меняет длину.

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

Edit2:

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

for (var я in a) console.log(typeof i) показывает 'string' для всех индексов.

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

От MDN:

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

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

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

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

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

Изменить 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:

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

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

Ответ 4

В JavaScript есть два типа массивов: стандартные массивы и ассоциативные массивы (или объект с собственностью)

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

Итак...

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

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

Ответ 5

Посмотрим:

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

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

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

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

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

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