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

Как лучше всего использовать массив JavaScript с непереходными индексами?

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

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

Например:

var myObjects = [] ;

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Чтобы ответить на вопрос OP: он не занимает больше места (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]] "массивом", а затем создает каждый элемент из 0 < я < len - значение undefined и т.д. Они просто оказываются undefined, когда метод toString выполняет итерацию по массиву. Это в основном означает , они не существуют.

11.2.1 Аксессоры свойств

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

  • Пусть baseReference будет результатом оценки MemberExpression.
  • Пусть baseValue будет GetValue (baseReference).
  • Пусть свойствоNameReference является результатом вычисления выражения.
  • Пусть свойствоNameValue будет GetValue (propertyNameReference).
  • Вызов CheckObjectCoercible (baseValue).
  • Пусть свойствоNameString будет toString (propertyNameValue).
  • Если оцениваемая синтаксическая продукция содержится в коде строгого режима, пусть строгое true, иначе пусть строгое false.
  • Возвращает значение типа Reference, базовым значением которого является baseValue, а ссылочным именем является свойствоNameString и строгий флаг строгого режима.

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

ECMA-262 5th Edition ( http://www.ecma-international.org/publications/standards/Ecma-262.htm )

Ответ 2

Здесь вы можете просто использовать здесь объект, имеющий ключи как целые числа, например:

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]);
  }
}

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

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

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

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

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

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

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

Ответ 3

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

var obj = {};
obj[471] = {foo: "bar"} ;

Ответ 4

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

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

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

var myObjects = {} ;

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

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

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

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


EDIT:

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

var someObj = {};

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

someObj[10] = 'val';

console.log(someObj);

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

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

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

Ответ 5

Как я понимаю из моего чтения Crockford "The Good Parts", это не особенно отнимает память, поскольку массивы javascript больше похожи на специальный тип коллекции значений ключей, чем фактический массив. Длина массива определяется не как количество адресов в фактическом массиве, а как индекс с наивысшим номером в массиве.

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

Ответ 6

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

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

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

Ответ 7

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

    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