我正在使用JavaScript编写Google Chrome扩展程序,我想使用数组存储一堆对象,但我希望索引是特定的非连续 ID号。
(这是因为我需要能够以后使用来自我控制之外的其他来源的ID号有效地查找值。)
例如:
var myObjects = [] ;
myObjects[471] = {foo: "bar"} ;
myObjects[3119] = {hello: "goodbye"}
当我console.log(myObjects)
时,在控制台中我看到整个阵列打印出来,所有数千个“缺失”索引显示undefined
。
我的问题是:这有关系吗?这会浪费任何记忆吗?
即使它没有浪费内存,当我循环遍历数组时,如果我必须手动跳过每个缺失值,它会浪费CPU吗?
我尝试使用对象而不是数组,但似乎你不能使用数字作为对象键。我希望有更好的方法来实现这一目标吗?
答案 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 [表达式]的评估如下:
GetValue
( baseReference )。 GetValue
( propertyNameReference )。 CheckObjectCoercible
( baseValue )。 ToString
( propertyNameValue )。 Reference
的值,其基值为 baseValue ,其引用名称为 propertyNameString ,其严格模式标志为 strict < / em>的。 生产 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