我一直在玩javascript数组而且我遇到过,我觉得有些不一致,我希望有人可以帮我解释一下。
让我们从这开始:
var myArray = [1, 2, 3, 4, 5];
document.write("Length: " + myArray.length + "<br />");
for( var i in myArray){
document.write( "myArray[" + i + "] = " + myArray[i] + "<br />");
}
document.write(myArray.join(", ") + "<br /><br />");
Length: 5 myArray[0] = 1 myArray[1] = 2 myArray[2] = 3 myArray[3] = 4 myArray[4] = 5 1, 2, 3, 4, 5
这段代码没有什么特别之处,但是我知道javascript数组是一个对象,因此可能会向数组添加特性,这些特性添加到数组的方式似乎与我不一致。
在继续之前,请注意如何在javascript中将字符串值转换为数字值。
的
的 的非空字符串 - &gt;字符串或NaN的数字值
空字符串 - &gt; 0
因此,由于javascript数组是一个对象,因此以下是合法的:
myArray["someThing"] = "someThing";
myArray[""] = "Empty String";
myArray["4"] = "four";
for( var i in myArray){
document.write( "myArray[" + i + "] = " + myArray[i] + "<br />");
}
document.write(myArray.join(", ") + "<br /><br />");
Length: 5 myArray[0] = 1 myArray[1] = 2 myArray[2] = 3 myArray[3] = 4 myArray[4] = four myArray[someThing] = someThing myArray[] = Empty String 1, 2, 3, 4, four
输出是意料之外的。
当设置属性myArray [“4”]时,非空字符串“4”被转换为其数值,这似乎是正确的。但是,空字符串“”不会转换为其数字值0,它将被视为空字符串。此外,非空字符串“something”未转换为其数值NaN,它被视为字符串。那是哪个呢?是数字或字符串上下文中myArray []内的语句吗?
另外,为什么myArray.length和myArray.join(“,”)中不包含myArray的两个非数字特性?
答案 0 :(得分:12)
JavaScript数组的键实际上是字符串。有关任意键的详细信息和地图类型的实现,请选中this answer。
澄清并添加Jason发布的内容:JavaScript数组是对象。对象具有属性。属性名称是字符串值。因此,在更多事情发生之前,数组索引也会转换为字符串。如果以下属性(ECMA-262,15.4),属性名称P将被视为数组索引(即将调用特殊数组魔术):
ToString(ToUint32(P))等于P而ToUint32(P)不等于2 ^ 32 - 1
可以轻松验证数字索引将转换为字符串(而不是相反):
var array = [];
array[1] = 'foo';
array['1'] = 'bar';
array['+1'] = 'baz';
document.writeln(array[1]); // outputs bar
此外,使用for..in
循环遍历数组条目的不良做法 - 如果有人与某些原型混淆(并且它也不是很快),您可能会得到意想不到的结果。请改用标准for(var i= 0; i < array.length; ++i)
。
答案 1 :(得分:2)
(编辑:以下不太正确)
JavaScript Object 的键实际上是字符串。 Javascript Array 本身具有数字索引。如果您存储具有可以解释为非负整数的索引的东西,它将尝试这样做。如果存储的索引不是非负整数(例如,它是字母数字,负数或带小数部分的浮点数),它将在数组索引存储上失败,并默认为对象(这是Array的基类)存储,然后将参数转换为字符串并按字符串索引存储 - 但这些存储的属性不会被Array类看到,因此其方法/属性不可见(长度,连接,切片,拼接,推送,弹出等)。
编辑:上面的内容不太正确(正如克里斯托弗的foo / bar / baz示例所示)。事实上,根据ECMAscript spec的实际存储索引是字符串,但如果它们是有效的数组索引(非负整数),则Array对象的[[Put]]
方法是特殊的,使这些特定值可见到Array的“array-ish”方法。
答案 2 :(得分:2)
这是对PhiLo's post的回答。他的基准是有缺陷的,因为他为对象版本使用了不同的属性名称:他应该使用i
而不是x
。
如果操作正确,例如:
var start, end, count = 1000000;
var obj = {},
array = [];
start = new Date;
for(var i = count; i--; )
array[i] = i;
end = new Date;
document.writeln(Number(end) - Number(start));
start = new Date;
for(var i = count; i--; )
obj[i] = i;
end = new Date;
document.writeln(Number(end) - Number(start));
你会发现时间非常接近。在FF3.0.5中,阵列版本甚至一直较慢(在Opera中,它是相反的)。
答案 3 :(得分:0)
当他声明“数组索引被转换为字符串”时,我不同意Christoph。
首先,我认为它依赖于实现...我认为(好的)实现者将优化数组访问,有一些聪明的方法可以做到。
实际上,我做了一点测试,虽然它和大多数微基准测试一样好(即不是超级可靠),但很有意思:
result = ""
var x;
var trueArray = []
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "i" + i; // To do the same operations
trueArray[i] = 1;
}
var endTime = new Date();
result += "With array: " + (endTime - startTime) + "\n";
var sArray = []
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "" + i;
sArray[x] = 1;
}
var endTime = new Date();
result += "With s array: " + (endTime - startTime) + "\n";
var objArray = {}
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "i" + i;
objArray[x] = 1;
}
var endTime = new Date();
result += "With object(i): " + (endTime - startTime) + "\n";
var sobjArray = {}
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "" + i;
sobjArray[x] = 1;
}
var endTime = new Date();
result += "With s object: " + (endTime - startTime) + "\n";
var iobjArray = {}
var startTime = new Date();
for (var i = 0; i < 100000; i++)
{
x = "" + i;
iobjArray[i] = 1;
}
var endTime = new Date();
result += "With i object: " + (endTime - startTime) + "\n";
// Then display result
在IE6上,我得到:数组:1453对象:3547
在FF 3.0上,我得到:使用数组:83对象:226
在Safari 3.1上,我得到:使用数组:140对象:313
在Opera 9.26上,由于某些原因我没有得到结果,但如果我减少到循环数的十分之一,我得到:使用数组:47对象:516
实际上,当我输入时,我让Opera运行,最后得到了结果:数组:281对象:166063 ...
因此阵列得到优化!哪个是幸运的...
克里斯托夫的示威并没有让我印象深刻。我的结论更多的是可以解释为数字的字符串被视为符合引用公式的字符串......
所以我对你的结果的解释是,当用这些数据进给时,数组表现得像一个带有数字索引的快速数组(可能是稀疏值上的关联数组行为,即一些孤立的大索引),但是作为一个对象,它仍然具有正常的属性处理。但是这些属性不在数组部分中处理,因此使用join()得到了结果。
[编辑]我按照克里斯托夫的想法添加了一些循环 在FF3上,我得到:使用数组:92使用s数组:93使用对象(i):243使用s对象:194使用i对象:125(穿孔在运行之间变化,但大致一致)。
我并不十分相信这个整数 - &gt; string - &gt;整数往返,甚至不是ECMA请求此序列。我读它的方式是:属性是一个字符串,可以解释为整数,然后它被视为这样。
当然,唯一确定的方法就是查看实现......
我感兴趣地注意到,获取整数属性的普通对象或可以转换为整数的属性以某种方式进行了优化。也许是因为许多JS程序员使用普通对象作为数组,所以实施者认为有兴趣优化这种情况。
答案 4 :(得分:0)
与JavaScript中的其他内容一样,数组是对象。对象已经被点符号祝福,以减轻开发人员的负担。使用您的示例
myArray["someThing"] = "someThing";
与写作相同
myArray.someThing = "someThing";
在这种情况下,您要在对象上添加属性,而不是将其添加到数组中。空字符串也是如此,尽管你不能将点符号用于空字符串......很奇怪,呵呵?
在“4”的情况下,它可以被强制转换为整数,因此被用作数组的索引。