Управление памятью JavaScript-массива

Возможный дубликат:
Являются ли массивы Javascript разреженными?

Я изучаю JavaScript на данный момент и читаю несколько простых введений и руководств. Когда я смотрел на объект Array, я наткнулся на некоторые подробности, которые поразили меня как очень странное, исходя из других языков, таких как C/Java/ Scala/...


Итак, давайте предположим, что мы определяем массив как таковой:

var arr = ['foo','bar','qux']

Теперь мы присваиваем

arr[5] = 'baz'

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

arr
>> ["foo", "bar", "qux", undefined, undefined, "baz"]

И длина как ожидалось

arr.length
>> 6

JavaScript любезно расширил наш массив до необходимой длины - шесть, а новые элементы установлены на undefined - за исключением того, на которое мы действительно присвоили значение.

С точки зрения низкого уровня это ужасно по памяти. Обычно массив будет представлять собой непрерывный диапазон в памяти, что делает массив более крупным, как правило, включает в себя копирование всего массива в новую ячейку памяти, достаточную по размеру. Это очень дорогостоящая операция.

Теперь я понимаю, что это, скорее всего, не, что делают движки JavaScript, поскольку копирование вокруг массивов будет сумасшедшим, а пространство памяти будет потрачено впустую на все эти значения undefined.

Может кто-нибудь сказать мне, что на самом деле происходит за дверью?

  • Являются ли массивы фактически связанными списками со списком?
  • Являются ли элементы массива 'undefined' на самом деле?
  • Как дорого работать с большими массивами, которые в основном заполнены "undefined"?

Ответы

Ответ 1

В первой версии JavaScript не было массивов. Позднее они были представлены в качестве подкласса этой "матери всех объектов": Object. Вы можете легко протестировать это, выполнив следующее:

var foo = [1,2,3,4];
for (var n in foo)
{//check if n is equal (value and type) to itself, coerced to a number
    console.log(n === +(n) ? 'Number' : 'String');
}

Это будет записывать String снова и снова. Внутренне все цифровые клавиши преобразуются в строки. Свойство Length просто извлекает самый высокий индекс и добавляет к нему 1. Больше ничего. Когда вы показываете свой массив, объект повторяется, и для каждого ключа применяются те же правила, что и для любого объекта: сначала проверяется экземпляр, а затем прототип (ы)... поэтому, если мы немного изменим наш код:

var foo = [1,2,3,4];
foo[9] = 5;
for (var n in foo)
{
    if (foo.hasOwnProperty(n))
    {//check if current key is an array property
        console.log(n === +(n) ? 'Number' : 'String');
    }
}

Вы заметите, что массив имеет только 5 собственных свойств, ключи undefined 4-8 - undefined, потому что не было соответствующего значения, найденного в экземпляре, и ни в одном из базовых прототипов. Короче: массивы - это не массивы, а объекты, которые ведут себя одинаково.

Как заметил Тим, вы можете иметь экземпляр массива с свойством undefined, которое существует внутри этого объекта:

var foo = [1,2,undefined,3];
console.log(foo[2] === undefined);//true
console.log(foo[99] === undefined);//true

Но опять-таки есть разница:

console.log((foo.hasOwnProperty('2') && foo[2] === undefined));//true
console.log((foo.hasOwnProperty('99') && foo[99] === undefined));//false

RECAP, ваши три основных вопроса:

  • Массивы - это объекты, которые позволяют ссылаться на их свойства с числовыми экземплярами

  • Значения undefined не существуют, они просто являются возвращаемым значением по умолчанию, когда JS сканирует объект и прототипы и не может найти то, что вы ищете: "Извините, что вы меня спрашиваете undefined в моей книге." это то, что он говорит.

  • Работа с массивными массивами undefined не влияет на размер самого объекта, но доступ к ключу undefined может быть очень, очень незначительно медленнее, потому что необходимо также проверять прототипы.

Update:

Просто цитируя Ecma std:

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

Объект, O, называется разреженным, если следующий алгоритм возвращает true:
1. Пусть len является результатом вызова внутреннего метода [[Get]] O с аргументом "length".
2. Для каждого целого я в диапазоне 0≤i
    а. Пусть elem является результатом вызова внутреннего метода O [[GetOwnProperty]] с аргументом       ToString (я).
    б. Если elem undefined, верните true.
3. Верните значение false.

Ответ 2

Массивы - это просто упорядоченный список объектов. В JavaScript все - объект, поэтому массивы не являются массивами, как мы их знаем:)

Вы можете найти небольшие внутренние элементы здесь.

За ваши сомнения относительно работы с большими массивами... Ну, помните, что чем меньше расчет вы делаете "на стороне клиента", тем быстрее будет ваша страница.

Ответ 3

Ответы:

  • Массив в JavaScript точно такой же, как объект (т.е. неупорядоченный набор свойств) с магическим свойством length и дополнительными методами прототипа (push() и т.д.).
  • Нет, элементов undefined нет. JavaScript имеет оператор in, который проверяет наличие свойства, которое вы можете использовать для доказательства этого. Итак, для следующего массива: var arr = ['foo']; arr[2] = 'bar';, 2 in arr возвращает true и 1 in arr возвращает false.
  • Редкий массив должен занимать больше памяти, чем плотный массив, длина которого представляет собой количество свойств, фактически определенных в вашем разреженном массиве. Это будет более дорого работать с разреженным массивом, когда вы перебираете его свойства undefined.

Ответ 4

Большинство реализаций javascript реализуют массивы как некоторый аромат двоичного дерева или хеш-таблицы с индексом массива в качестве ключа, поэтому большой диапазон объектов undefined не использует никакой памяти.

Ответ 5

Мне сказали, что массивы входят в 2 части, [значение, указатель]. Таким образом, указатель arr [2] является нулевым. Когда вы добавляете 5, он меняет адрес от нуля до точки до номера 3, который указывает на номер 4, который указывает на номер 5, который является нулевым (так что конец массива).

Я не уверен, насколько это верно, поскольку он никогда не проверял его. Но, похоже, это имеет смысл.

Таким образом, вы не можете выполнять математику, например, в массиве ac type (то есть, чтобы получить значение 4, просто введите начальную точку памяти + 4x (объем объекта в памяти)), но вы можете сделать это, следуя массиву peice by peice