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

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

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

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

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

2 ответов


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

что означает, утверждение типа:

var arr = new Array(100000);

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

var arr = [];

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

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

например:

вы можете создать пустой массив и добавить два элемента с индексом 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, а также изменять любой элемент в массиве на какой-либо другой тип. Как интерпретатор отслеживает, в каком месте физической памяти находится какой-либо элемент? Кроме того, как предотвратить перезапись данных в следующем элементе, если я изменю элемент на более крупный тип данных?

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

--

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

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

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

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 в качестве смежных блоков памяти, вы должны изучить использование TypedArray. TypedArray позволяет выделить блок памяти в виде массива байтов и более эффективно получить доступ к необработанным двоичным данным.

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


вот немного пищи для размышлений. Я сделал a jsperf для тестирования простой оптимизации массива что некоторые движки JavaScript реализовать.

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

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

The 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 раза быстрее.

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