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

Как представлены массивы JavaScript в физической памяти?

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

Я предполагаю, что массивы хранят только ссылки на фактические объекты, а примитивы завершаются за кулисами при размещении в массивах.

Предполагая, что это так, если у меня есть другой дескриптор примитивной переменной и изменение значения, хранящегося в массиве, поддерживается синхронность?

Я знаю, что я, вероятно, уже ответил на свой вопрос, но я точно не знаю, и я не могу найти никакой информации по этому вопросу.

4b9b3361

Ответ 1

Обычно массивы выделяют непрерывный блок памяти фиксированной длины. Однако в Javascript массивы представляют собой типы объектов со специальными конструкторами и методами доступа.

Это означает, что оператор вроде:

var arr = new Array(100000);

не выделяет никакой памяти! Фактически, он просто устанавливает значение свойства length в массиве. Когда вы создаете массив, вам не нужно объявлять размер по мере его роста. Итак, вы должны использовать это вместо:

var arr = [];

Массивы в Javascript разрежены, что означает, что не все элементы в массиве могут содержать данные. Другими словами, в массиве существуют только те элементы, которые содержат фактические данные. Это уменьшает объем памяти, используемой массивом. Значения располагаются ключом, а не смещением. Это просто метод удобства и не предназначен для комплексного численного анализа.

Массивы в Javascript не печатаются, поэтому значение элемента может быть объектом, строкой, числом, булевым, функцией или массивом. Основное различие между массивом и объектом - это свойство длины, которое имеет значение, большее, чем наибольший целочисленный ключ в массиве.

Например:

Вы могли бы создать пустой массив и добавить два элемента в индекс 0 и индекс 99. Длина будет равна 100, но количество элементов в массиве будет равно 2.

var arr = [];
arr[0] = 0;
arr[99] = {name: "John"};
console.log(arr.length); // prints 100
arr; // prints something like [0, undefined × 98, Object { name: "John"}]

Чтобы ответить на ваши вопросы напрямую:

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

а. Вероятно, вы уже знаете это, если вы прочитали мои комментарии выше. В Javascript массив является типом Hashtable Object, поэтому интерпретатору не нужно отслеживать физическую память, а изменение значения элемента не влияет на другие элементы, поскольку они не хранятся в непрерывном блоке памяти.

-

Q. Я предполагаю, что массивы только хранят ссылки на реальные объекты, а примитивы завершаются за кулисами при размещении в массивах. Предполагая, что это так, если у меня есть другой дескриптор примитивной переменной и изменение значения, хранящегося в массиве, поддерживается синхронность?

а. Нет, примитивы не завернуты. Изменение примитива, назначенного массиву, не изменит значение в массиве, поскольку оно хранится по значению. Объекты с другой стороны хранятся по ссылке, поэтому изменение значения объектов будет отражать это изменение в этом массиве.

Вот пример, который вы можете попробовать:

var arr = [];
var obj = { name: "John" };
var isBool = true;

arr.push(obj);
arr[1] = isBool;

console.log(arr[0]); // print obj.name
console.log(arr[1]); // print true

obj.age = 40;        // add age to obj
isBool = false;      // change value for isBool

console.log(arr[0]); // value here will contain age
console.log(arr[1]); // value here will still be true

Также обратите внимание, что при инициализации массива двумя способами он имеет другое поведение:

var arr = new Array(100);
console.log(arr.length);        // prints 100
console.log(arr);               // prints []

var arr2 = new Array(100, 200);
console.log(arr2.length);       // prints 2
console.log(arr2);              // prints [100, 200]

Если вы хотите использовать Javascript Arrays как непрерывные блоки памяти, вы должны изучить TypedArray. TypedArray позволяет вам выделять блок памяти в виде байтового массива и более эффективно обращаться к исходным двоичным данным.

Вы можете узнать больше о тонкостях Javascript, прочитав спецификацию ECMA-262 (версия 5.1).

Ответ 2

Вот пища для размышлений. Я сделал jsperf для тестирования простой оптимизации массива, которую реализуют некоторые движки JavaScript.

Тестовый пример создает два массива по миллиону элементов в каждом. Массив содержит только цифры; a массив b содержит те же числа, за исключением первого элемента, который является объектом:

var a = [ 0 ], b = [ { valueOf: function() { return 0; } } ];
for( var i = 1;  i < 1000000;  ++i ) {
    a[i] = b[i] = i;
}

Свойство valueOf объекта в первом элементе массива b возвращает 0 поэтому арифметика будет такой же, как в первом массиве.

Затем два теста просто суммируют все значения для двух массивов.

Быстрый массив:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += a[i];
}

Медленный массив:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += b[i];
}

Как видно из результатов теста в jsperf, в Chrome числовой массив примерно в 5 раз быстрее, в Firefox числовой массив примерно в 10 раз быстрее, а в IE он примерно в 2 раза быстрее.

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

Ответ 3

Принятый ответ говорит, что

[Конструкция массива] не выделяет никакой памяти!

Это не обязательно правда. Хранение массивов в виде неупорядоченных пар ключ-значение, как в объектах, очень неэффективно, поскольку каждый поиск является поиском. Даже если записи были отсортированы, он все равно выделил бы больше памяти, чем необходимо. Поэтому многие движки представляют массивы несколькими различными способами для сохранения памяти и оптимизации производительности. Это также можно увидеть в примере Майкла Гири: оптимизируется массив, содержащий только числа (подробности для V8). И иногда механизм может решить выделить несколько пустых слотов для вновь созданного массива, если базовое представление имеет фиксированный размер и не может быть так легко масштабировано.