Как лучше всего сделать массив JavaScript с непоследовательными индексами? - PullRequest
19 голосов
/ 23 января 2011

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

(Это потому, что мне нужно иметь возможность эффективно искать значения позже, используя идентификационный номер, полученный из другого источника вне моего контроля.)

Например:

var myObjects = [] ;

myObjects[471] = {foo: "bar"} ;

myObjects[3119] = {hello: "goodbye"}

Когда я делаю console.log(myObjects), в консоли я вижу весь распечатанный массив со всеми тысячами «отсутствующих» индексов, показывающих undefined.

Мой вопрос: это имеет значение? Это пустая трата памяти?

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

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

Ответы [ 7 ]

13 голосов
/ 23 января 2011

Прежде всего, все, пожалуйста, узнайте, что то, что делает for-in statement, называется перечислением (хотя это IterationStatement ), чтобы отличаться от итерация . Это очень важно, потому что это приводит к путанице, особенно среди начинающих.

Чтобы ответить на вопрос ОП: он не занимает больше места ( test ) (можно сказать, что это зависит от реализации, но мы говорим о расширении Google Chrome! ) и тоже не медленнее ( test ).

Тем не менее, мой совет: Используйте то, что подходит! В этой ситуации: используйте объекты !

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

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

// keys are converted to strings 
// (numbers can be used safely)
var obj = {}
obj[1] = "value"
alert(obj[1])   // "value"
alert(obj["1"]) // "value"

Примечание к разреженным массивам

Основная причина, по которой разреженный массив НЕ будет тратить пространство , заключается в том, что в спецификации этого не говорится. Нет смысла требовать, чтобы средства доступа к свойствам проверяли, является ли внутреннее свойство [[Class]] "Array" , а затем создавали каждый элемент из 0 значение undefined и т. д. Они просто оказываются равными undefined, когда метод toString выполняет итерации по массиву. Это в основном означает их там нет .

11.2.1 Средства доступа к собственности

Производство MemberExpression : MemberExpression [ Выражение ] оценивается следующим образом:

  1. Пусть baseReference будет результатом вычисления MemberExpression .
  2. Пусть baseValue будет GetValue ( baseReference ).
  3. Пусть propertyNameReference будет результатом вычисления Выражения .
  4. Пусть propertyNameValue будет GetValue ( propertyNameReference ).
  5. Вызов CheckObjectCoercible ( baseValue ).
  6. Пусть propertyNameString будет ToString ( propertyNameValue ).
  7. Если оцениваемая синтаксическая продукция содержится в коде строгого режима, пусть строгий будет истинным , иначе пусть строгий будет ложным .
  8. Возвращает значение типа Reference, базовое значение которого baseValue и чье ссылочное имя propertyNameString , а флаг строгого режима - строгий .

Производство CallExpression : CallExpression [ Выражение ] оценивается точно так же, за исключением того, что оценивается содержащееся CallExpression на шаге 1.

ECMA-262, 5-е издание (http://www.ecma -international.org / публикации / стандарты / Ecma-262.htm )

5 голосов
/ 23 января 2011

Вы можете просто использовать здесь вместо этого объект с ключами в виде целых чисел, например:

var myObjects = {};
myObjects[471] = {foo: "bar"};
myObjects[3119] = {hello: "goodbye"};

Это позволяет вам хранить что-либо в объекте, функциях и т. Д. Для перечисления (поскольку это объект сейчас) поверх него вам потребуется другой синтаксис, цикл for...in, например:

for(var key in myObjects) {
  if(myObjects.hasOwnProperty(key)) {
    console.log("key: " + key, myObjects[key]);
  }
}

По другим вашим конкретным вопросам:

Мой вопрос: это имеет значение? Это пустая трата памяти?

Да, он тратит немного памяти на распределение (тем более для итерации) - не так много, имеет ли это значение ... это зависит от того, насколько разнесены ключи.

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

Да, здесь используются дополнительные циклы.

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

Конечно, вы можете !, см. Выше.

3 голосов
/ 23 января 2011

Я бы использовал объект для их хранения.Вы можете использовать числа для свойств, используя нижнюю запись, но вы не можете использовать точечную запись;объект, переданный в качестве ключа с использованием индексной записи, имеет вызов toString().

2 голосов
/ 23 января 2011

Насколько я понимаю из моего прочтения Крокфорда «Хорошие части», это не особенно тратит память, так как массивы javascript больше похожи на специальный вид сбора значений ключей, чем на реальный массив. Длина массива определяется не как количество адресов в реальном массиве, а как индекс с наибольшим номером в массиве.

Но вы правы, перебирая все возможные значения, пока не доберетесь до длины массива. Лучше сделать, как вы упоминаете, и использовать объект с цифровыми клавишами. Это возможно, используя синтаксис myObj['x']=y, где x - символ для некоторого целого числа. например myObj['5']=poodles По сути, конвертируйте ваш индекс в строку, и вы можете использовать его в качестве ключа объекта.

2 голосов
/ 23 января 2011

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

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

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

var myObjects = {} ;

myObjects["471"] = {foo: "bar"} ;

myObjects["3119"] = {hello: "goodbye"};

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

Теперь вы будете использовать a for-in выражение для итерации по набору, и вы получите только те свойства, которые вы 'определено.


РЕДАКТИРОВАТЬ:

Что касается console.log() отображения индексов, которых не должно быть, вот пример того, как легко это сделатьобманным путем заставьте инструменты разработчика думать, что у вас есть массив.

var someObj = {};

someObj.length = 11;
someObj.splice = function(){};

someObj[10] = 'val';

console.log(someObj);

Очевидно, что это объект, но Firebug и инструменты разработчика Chrome отобразят его как массив с 11 членами.

[undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "val"]

Итак, вы можете видеть, что консоль не обязательно отражает, как / какие данные на самом деле хранятся.

0 голосов
/ 03 августа 2017

Вы можете попытаться сделать что-то вроде этого, чтобы сделать JIL-компилятор громким и понятным, что это более массивный объектный массив, например:

    window.SparseArray = function(len){
      if (typeof len === 'number')
        if (0 <= len && len <= (-1>>>0))
          this.length = len;
        else
          new Array(len); // throws an Invalid array length type error
      else
        this.push.apply(this, arguments);
    }
    window.SparseArray.prototype = Array.prototype
0 голосов
/ 23 января 2011

Я бы просто использовал постоянный префикс, чтобы избежать таких проблем.

var myObjects = {};
myObjects['objectId_'+365] = {test: 3};

по умолчанию будет Js-Objects.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...