如何最好地使用非连续索引执行JavaScript数组?

时间:2011-01-22 22:19:44

标签: javascript arrays object

我正在使用JavaScript编写Google Chrome扩展程序,我想使用数组存储一堆对象,但我希望索引是特定的非连续 ID号。

(这是因为我需要能够以后使用来自我控制之外的其他来源的ID号有效地查找值。)

例如:

var myObjects = [] ;

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

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

当我console.log(myObjects)时,在控制台中我看到整个阵列打印出来,所有数千个“缺失”索引显示undefined

我的问题是:这有关系吗?这会浪费任何记忆吗?

即使它没有浪费内存,当我循环遍历数组时,如果我必须手动跳过每个缺失值,它会浪费CPU吗?

我尝试使用对象而不是数组,但似乎你不能使用数字作为对象键。我希望有更好的方法来实现这一目标吗?

7 个答案:

答案 0 :(得分:12)

首先,每个人,请了解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]]属性是否为“Array”,然后从 0< 0创建每个元素。我< len 是值undefined等。当undefined方法迭代数组时,它们恰好是toString。它基本上意味着他们不在那里

11.2.1属性访问者

生产 MemberExpression MemberExpression [表达式]的评估如下:

  1. baseReference 成为评估 MemberExpression 的结果。
  2. baseValue GetValue baseReference )。
  3. propertyNameReference 成为评估 Expression 的结果。
  4. propertyNameValue GetValue propertyNameReference )。
  5. 致电CheckObjectCoercible baseValue )。
  6. propertyNameString ToString propertyNameValue )。
  7. 如果正在评估的语法制作包含在严格模式代码中,请让严格 为真,否则让严格 即可。
  8. 返回类型Reference的值,其基值为 baseValue ,其引用名称为 propertyNameString ,其严格模式标志为 strict < / em>的。
  9. 生产 CallExpression CallExpression [ Expression ]的评估方式完全相同,只是包含 CallExpression 在步骤1中进行评估。

    ECMA-262第5版 http://www.ecma-international.org/publications/standards/Ecma-262.htm

答案 1 :(得分:4)

你可以在这里简单地使用一个对象,将键作为整数,如下所示:

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

对于您的其他具体问题:

  

我的问题是:这有关系吗?这会浪费任何记忆吗?

是的,它浪费了一些内存用于分配(更多 - 因此迭代它) - 虽然不多,但这很重要......这取决于密钥的间隔距离。

  

即使它没有浪费内存,当我循环遍历数组时,如果我必须手动跳过每个缺失值,它会浪费CPU吗?

是的,这里使用额外的周期。

  

我尝试使用对象而不是数组,但似乎你不能使用数字作为对象键。我希望有更好的方法来实现这个目标吗?

当然可以!,见上文。

答案 2 :(得分:3)

我会使用一个对象来存储它们。您可以使用下标表示法使用数字作为属性,但不能使用点表示法;使用下标符号作为键传递的对象已调用toString()

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

答案 3 :(得分:2)

这将取决于实现,但我认为您不必担心“介于两者之间”索引的内存浪费。开发人员工具并不代表数据的存储方式。

关于迭代它们,是的,当你使用for循环时,你会迭代它们之间的所有内容。

如果顺序顺序不重要,那么一定要使用普通的Object而不是Array。是的,您可以为属性使用数字名称。

var myObjects = {} ;

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

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

在这里,我使用字符串作为名称,因为你说你的数字有问题。无论如何,当你循环时,它们最终会表示为字符串。

现在,您将使用a for-in statement迭代该集合,并且您只会获得已定义的属性。


修改

关于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"]

因此,您可以看到控制台不一定反映实际存储的数据/数据。

答案 4 :(得分:2)

据我所知,在我阅读Crockford的“The Good Parts”时,这并没有特别浪费内存,因为javascript数组更像是一种特殊的键值集合,而不是实际的数组。数组的长度不是定义为实际数组中的地址数,而是定义为数组中编号最大的索引。

但你是正确的,迭代所有可能的值,直到你达到数组的长度。最好像你提到的那样做,并使用带有数字键的对象。这可以通过使用语法myObj['x']=y来实现,其中x是某个整数的符号。例如myObj['5']=poodles基本上,将索引转换为字符串,您可以将其用作对象键。

答案 5 :(得分:0)

我只想使用常量前缀,以避免此类问题。

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

将默认为Js-Objects。

答案 6 :(得分:0)

您可以尝试执行类似这样的操作,使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