我有一个关于JavaScript数组的一般性问题。 JavaScript中的数组索引是否作为字符串内部处理?我读到某处,因为数组是JavaScript中的对象,索引实际上是一个字符串。我对此感到有点困惑,并且很乐意解释。
答案 0 :(得分:13)
形式上,所有属性名称都是字符串。这意味着类似数组的数字属性名称与任何其他属性名称没有任何区别。
如果查看步骤6 in the relevant part of the spec,您会看到在查找属性之前,属性访问器表达式始终强制转换为字符串。无论对象是数组实例还是其他类型的对象,都遵循(正式)该过程。 (同样,它只是似乎就像发生了什么。)
现在,内部,JavaScript运行时可以随心所欲地实现数组功能。
编辑 - 我曾想过使用Number.toString
来演示数字到字符串的转换,但事实证明该规范明确地将特定的类型转换描述为通过内部流程发生,而不是通过隐式转换,然后调用.toString()
(这可能是出于性能原因的好事)。
答案 1 :(得分:4)
这是正确的:
> var a = ['a','b','c']
undefined
> a
[ 'a', 'b', 'c' ]
> a[0]
'a'
> a['0']
'a'
> a['4'] = 'e'
'e'
> a[3] = 'd'
'd'
> a
[ 'a', 'b', 'c', 'd', 'e' ]
答案 2 :(得分:3)
是的,技术上数组索引是字符串,但正如Flanagan优雅地将其放入他的“权威指南”中那样: "清楚地区分数组索引和对象属性名称是有帮助的。所有索引都是属性名称,但只有0到2之间的整数的属性名称 32 -1才是索引。"
通常你不应该关心浏览器(或者更常见的' script-host')在内部做什么,只要结果符合可预测的(通常/希望)指定的结果。实际上,在javascript(或ECMAScript 262)的情况下,仅根据需要的概念步骤来描述。 (故意)为脚本主机(和浏览器)留出了空间,以便用更聪明,更小,更快的方式来实现指定的行为。
事实上,现代浏览器在内部对不同类型的数组使用了许多不同的算法:它们包含什么,它们有多大,是否有序,如果它们是固定的并且可以在(jit)编译时进行优化 - 时间或者它们是稀疏的还是密集的(是的,通常需要支付new Array(length_val)
代替忍者[]
)。
在你的思维概念(学习javascript时)中,知道数组只是特殊类型的对象可能会有所帮助。但它们不总是与人们期望的相同,例如:
var a=[];
a['4294967295']="I'm not the only one..";
a['4294967296']="Yes you are..";
alert(a); // === I'm not the only one..
尽管对于不知情的程序员来说,拥有一个数组(带索引)并将属性附加到数组对象上是非常容易和非常透明的。
最佳答案(我认为)来自spec (15.4)本身:
数组对象
数组对象对某类属性进行特殊处理 名。 属性名称P(以String值的形式)是一个数组 索引当且仅当ToString(ToUint32(P))等于P和 ToUint32(P)不等于2 32 -1 。属性名称为的属性 数组索引也称为元素。每个Array对象都有一个 length属性,其值始终是小于的非负整数 2 32 。 length属性的值在数值上大于名称为数组索引的每个属性的名称;每当一个 创建或更改Array对象的属性,以及其他属性 根据需要进行调整以保持这种不变性。特别, 每当添加名称为数组索引的属性时,长度 如有必要,属性会更改为数字以外的属性 该数组索引的值;每当长度属性是 已更改,每个属性的名称都是数组索引,其值为 不小于新长度会自动删除。这个 约束仅适用于Array对象的自身属性,并且是 不受可能继承的长度或数组索引属性的影响 来自它的原型。
如果以下算法返回,则称对象O为稀疏 真:
- 设len是使用参数" length"来调用O的[[Get]]内部方法的结果。
对于0≤i范围内的每个整数i
一个。让elem成为使用参数ToString(i)调用O的[[GetOwnProperty]]内部方法的结果。
湾如果elem未定义,则返回true。- 醇>
返回false。
有效地,ECMAScript 262规范只确保javascript程序员明确的数组引用,无论获取/设置arr['42']
或arr[42]
最多32位无符号。
主要区别在于(例如(自动更新)array.length
,array.push
和其他类似糖array.concat
等。
虽然,是的,javascript还允许一个循环覆盖已设置为对象的属性,但我们无法读取已设置的数量(没有循环)。 是的,据我所知,现代浏览器(特别是他们称之为chrome(但并未完全指定))'小整数'使用true(预初始化的)小型int数组快速进行恶意攻击。
另请参阅示例this相关问题。
根据@Felix Kling的测试编辑(来自上面的评论):
在arr[4294967294] = 42;
之后,arr.length
正确显示4294967295
。但是,请致电arr.push(21)
;抛出RangeError: Invalid array length
。 arr[arr.length] = 21
有效,但不会改变长度。
在这个答案之后,应该清楚解释这个(可预测的和预期的)行为。
<强> EDIT2:强>
现在,有人发表了评论:
for(var i in a)console.log(typeof i)显示&#39; string&#39;对于所有索引。
由于for in
是javascript中的(无序的I 必须 add)属性迭代器,因此很明显它会返回一个字符串(如果它没有&我就会非常愚蠢#39; t)的
来自MDN:
for..in不应该用于迭代索引顺序的数组 很重要。
数组索引只是具有整数名称的可枚举属性 否则与一般对象属性相同。没有 保证for ... in将返回任何特定的索引 order,它将返回所有可枚举的属性,包括那些 使用非整数名称和继承的名称。
因为迭代的顺序是依赖于实现的,所以迭代 在数组上可能无法以一致的顺序访问元素。因此 最好使用带有数字索引的for循环(或Array.forEach 当迭代数组的顺序时,或者循环的for ... 访问很重要。
那么......我们学到了什么?如果顺序对我们很重要(通常是数组),那么我们
现在想想另一种选择:给你的对象一个id / order但是你需要再次为每个下一个id / order(属性)遍历你的对象..
编辑3:
有人回答说:
var a = ['a','b','c'];
a['4'] = 'e';
a[3] = 'd';
alert(a); // returns a,b,c,d,e
现在使用我的回答中的解释:发生的事情是'4'
可以对整数4
进行强制,并且在[0, 4294967295]
范围内使其成为有效数组{{1}也称为index
。由于var element
是一个数组(a
),因此数组元素 4将作为数组元素添加,而不是作为属性添加(会发生什么)如果var []
是一个对象(a
)。
进一步概述数组和对象之间差异的示例:
{}
查看它如何返回var a = ['a','b','c'];
a['prop']='d';
alert(a);
,而不是&#39; d&#39;有待观察。
编辑4:
您注释:&#34;在这种情况下,整数索引应该作为字符串处理,因为它是数组的属性,这是一种特殊类型的JS对象。&#34; < / em>的
就术语而言,这是错误的,因为:(表示字符串)整数索引(在[0,4294967295]之间)创建数组a,b,c
或indexes
;不是elements
。
最好说:表示整数的实际整数和 a properties
(均在[0,42949672]之间)是有效的数组索引(并且应该在概念上被视为整数)并创建/更改数组元素(当您执行{{1}时返回的&#39;事物&#39; / values(仅)例如)或string
其他所有内容都会创建/更改属性(并且在概念上应该被视为字符串)
浏览器真正做的事情通常不应该让您感兴趣,注意到您编写的代码越简单明确,浏览器就越有可能识别出来:&#39;哦,让我们将它优化为真正的阵列& #39;
答案 3 :(得分:2)
在JavaScript中有两种类型的数组:标准数组和关联数组(或具有特性的对象)
所以......
var arr = [ 0, 1, 2, 3 ];
...被定义为索引只能是整数的标准数组。 arr [&#34; something&#34;]因为某些东西(你用作索引)不是一个整数,你基本上是为arr对象定义一个属性(一切都是JavaScript中的对象)。但是你没有在标准数组中添加元素。
答案 4 :(得分:2)
让我们看看:
[1]["0"] === 1 // true
哦,但这并不是决定性的,因为运行时可能会"0"
强制转换为+"0"
和+"0" === 0
。
[1][false] === undefined // true
现在,+false === 0
,所以不,运行时不会将值强制转换为数字。
var arr = [];
arr.false = "foobar";
arr[false] === "foobar" // true
实际上,运行时将值强制转换为字符串。所以,它是一个哈希表查找(外部)。