简体   繁体   English

JavaScript 数组索引是字符串还是整数?

[英]Is a JavaScript array index a string or an integer?

I had a generic question about JavaScript arrays.我有一个关于 JavaScript 数组的通用问题。 Are array indices in JavaScript internally handled as strings? JavaScript 中的数组索引是否在内部作为字符串处理?

I read somewhere that because arrays are objects in JavaScript, the index is actually a string.我在某处读到,因为数组是 JavaScript 中的对象,所以索引实际上是一个字符串。 I am a bit confused about this, and would be glad for any explanation.我对此有点困惑,很高兴能得到任何解释。

Formally, all property names are strings.正式地,所有属性名称都是字符串。 That means that array-like numeric property names really aren't any different from any other property names.这意味着类似数组的数字属性名称实际上与任何其他属性名称没有任何不同。

If you check step 6 in the relevant part of the spec , you'll see that property accessor expressions are always coerced to strings before looking up the property.如果您检查规范的相关部分中的第 6 步,您将看到在查找属性之前,属性访问器表达式总是被强制转换为字符串。 That process is followed (formally) regardless of whether the object is an array instance or another sort of object.无论对象是数组实例还是其他类型的对象,都会(正式)遵循该过程。 (Again, it just has to seem like that's what's happening.) (再一次,它看起来就像是正在发生的事情。)

Now, internally , the JavaScript runtime is free to implement array functionality any way it wants.现在,在内部,JavaScript 运行时可以自由地以任何方式实现数组功能。

edit — I had the idea of playing with Number.toString to demonstrate that a number-to-string conversion happens, but it turns out that the spec explicitly describes that specific type conversion as taking place via an internal process, and not by an implicit cast followed by a call to .toString() (which probably is a good thing for performance reasons).编辑——我想用Number.toString来演示数字到字符串的转换发生,但事实证明,规范明确地描述了特定的类型转换是通过内部过程发生的,而不是通过隐式cast 然后调用.toString() (出于性能原因,这可能是一件好事)。

That is correct so:这是正确的:

> 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' ]

Yes, technically array-indexes are strings, but as Flanagan elegantly put it in his 'Definitive guide': "It is helpful to clearly distinguish an array index from an object property name. All indexes are property names, but only property names that are integers between 0 and 2 32 -1 are indexes."是的,从技术上讲,数组索引是字符串,但正如 Flanagan 在他的“权威指南”中优雅地指出的那样:“明确区分数组索引和对象属性名称是有帮助的。所有索引都是属性名称,但只有属性名称是0 到 2 32 -1 之间的整数是索引。”

Usually you should not care what the browser (or more in general 'script-host') does internally as long as the outcome conforms to a predictable and (usually/hopefully) specified result.通常你不应该关心浏览器(或更一般的“脚本主机”)在内部做什么,只要结果符合可预测的和(通常/希望)指定的结果。 In fact, in case of JavaScript (or ECMAScript 262) is only described in terms of what conceptual steps are needed.事实上,在 JavaScript(或 ECMAScript 262)的情况下,仅根据需要哪些概念性步骤来描述。 That (intentionally) leaves room for script-host (and browsers) to come up with clever smaller and faster way's to implement that specified behavior.这(有意地)为脚本主机(和浏览器)提供了空间,以提出更小、更快的聪明方法来实现指定的行为。

In fact, modern browsers use a number of different algorithms for different types of arrays internally: it matters what they contain, how big they are, if they are in order, if they are fixed and optimizable upon (JIT) compile-time or if they are sparse or dense (yes it often pays to do new Array(length_val) instead of ninja [] ).事实上,现代浏览器在内部对不同类型的数组使用了许多不同的算法:重要的是它们包含什么、它们有多大、它们是否有序、它们是否在 (JIT) 编译时固定和优化,或者是否它们是稀疏或密集的(是的,使用new Array(length_val)而不是 ninja []通常是值得的)。

In your thinking-concept (when learning JavaScript) it might help to know that arrays are just special kind of objects.在您的思维概念中(在学习 JavaScript 时),了解数组只是一种特殊类型的对象可能会有所帮助。 But they are not always the same thing one might expect, for example:但它们并不总是人们所期望的相同,例如:

var a=[];
a['4294967295']="I'm not the only one..";
a['4294967296']="Yes you are..";
alert(a);  // === I'm not the only one..

although it is easy and pretty transparent to the uninformed programmer to have an array (with indexes) and attach properties to the array-object.尽管对于不知情的程序员来说,拥有一个数组(带有索引)并将属性附加到数组对象是很容易且相当透明的。

The best answer (I think) is from the specification (15.4) itself:最好的答案(我认为)来自规范(15.4)本身:

Array Objects数组对象

Array objects give special treatment to a certain class of property names.数组对象对特定类别的属性名称给予特殊处理。 A property name P (in the form of a String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2 32 −1 .属性名称 P(以字符串值的形式)是数组索引,当且仅当 ToString(ToUint32(P)) 等于 P 且 ToUint32(P) 不等于 2 32 -1 A property whose property name is an array index is also called an element.属性名称为数组索引的属性也称为元素。 Every Array object has a length property whose value is always a nonnegative integer less than 2 32 .每个 Array 对象都有一个 length 属性,其值始终是一个小于 2 32的非负整数。 The value of the length property is numerically greater than the name of every property whose name is an array index; length 属性的值在数字上大于名称为数组索引的每个属性的名称; whenever a property of an Array object is created or changed, other properties are adjusted as necessary to maintain this invariant.每当创建或更改 Array 对象的属性时,都会根据需要调整其他属性以保持此不变性。 Specifically, whenever a property is added whose name is an array index, the length property is changed, if necessary, to be one more than the numeric value of that array index;具体来说,每当添加名称为数组索引的属性时,如果需要,将长度属性更改为比该数组索引的数值大 1; and whenever the length property is changed, every property whose name is an array index whose value is not smaller than the new length is automatically deleted.每当更改长度属性时,名称为数组索引且值不小于新长度的每个属性都会自动删除。 This constraint applies only to own properties of an Array object and is unaffected by length or array index properties that may be inherited from its prototypes.此约束仅适用于 Array 对象的自身属性,不受可能从其原型继承的长度或数组索引属性的影响。

An object, O, is said to be sparse if the following algorithm returns true:如果以下算法返回 true,则对象 O 被称为稀疏对象:

  1. Let len be the result of calling the [[Get]] internal method of O with argument "length".令 len 为使用参数“length”调用 O 的 [[Get]] 内部方法的结果。

  2. For each integer i in the range 0≤i<ToUint32(len)对于 0≤i<ToUint32(len) 范围内的每个整数 i

    a.一种。 Let elem be the result of calling the [[GetOwnProperty]] internal method of O with argument ToString(i).令 elem 成为使用参数 ToString(i) 调用 O 的 [[GetOwnProperty]] 内部方法的结果。 b.If elem is undefined, return true.如果 elem 未定义,则返回 true。

  3. Return false.返回假。

Effectively the ECMAScript 262 spec just ensures to the JavaScript-programmer unambiguous array-references regardless of getting/setting arr['42'] or arr[42] up to 32-bit unsigned.有效地,ECMAScript 262 规范只是确保 JavaScript 程序员无歧义的数组引用,而不管获取/设置arr['42']arr[42]高达 32 位无符号。

The main difference is for example (auto-updating of) array.length , array.push and other array-sugar like array.concat , etc. While, yes, JavaScript also lets one loop over the properties one has set to an object, we can not read how much we have set (without a loop).主要区别是例如(自动更新) array.lengtharray.push和其他数组糖,如array.concat等。虽然,是的,JavaScript 还允许对已设置为对象的属性进行一次循环,我们无法读取我们设置了多少(没有循环)。 And yes, to the best of my knowledge, modern browsers (especially chrome in what they call (but don't exactly specify)) 'small integers' are wicked fast with true (pre-initialized) small-int arrays.是的,据我所知,现代浏览器(尤其是他们所称的 chrome(但没有具体说明))“小整数”对于真正的(预初始化的)小整数数组的速度非常快。

Also see for example this related question.另请参阅例如相关问题。

Edit: as per @Felix Kling's test (from his comment above):编辑:根据@Felix Kling 的测试(来自他上面的评论):

After arr[4294967294] = 42;arr[4294967294] = 42; , arr.length correctly shows 4294967295 . , arr.length正确显示4294967295 However, calling arr.push(21) ;但是,调用arr.push(21) throws a RangeError: Invalid array length .抛出RangeError: Invalid array length arr[arr.length] = 21 works, but doesn't change length. arr[arr.length] = 21有效,但不会改变长度。

The explanation for this (predictable and intended) behavior should be clear after this answer.在此答案之后,对这种(可预测的和预期的)行为的解释应该很清楚。

Edit2:编辑2:

Now, someone gave the comment:现在,有人发表了评论:

for (var i in a) console.log(typeof i) shows 'string' for all indexes. for (var i in a) console.log(typeof i) 显示所有索引的“字符串”。

Since for in is the (unordered I must add) property iterator in JavaScript, it is kind of obvious it returns a string (I'd be pretty darned if it didn't).因为for in是 JavaScript 中的(无序的,我必须添加)属性迭代器,很明显它返回一个字符串(如果没有,我会非常糟糕)。

From MDN :来自MDN

for..in should not be used to iterate over an Array where index order is important. for..in 不应用于迭代索引顺序很重要的数组。

Array indexes are just enumerable properties with integer names and are otherwise identical to general Object properties.数组索引只是具有整数名称的可枚举属性,在其他方面与一般对象属性相同。 There is no guarantee that for...in will return the indexes in any particular order and it will return all enumerable properties, including those with non–integer names and those that are inherited.无法保证 for...in 会以任何特定顺序返回索引,并且会返回所有可枚举属性,包括具有非整数名称的属性和继承的属性。

Because the order of iteration is implementation dependent, iterating over an array may not visit elements in a consistent order.因为迭代顺序取决于实现,所以迭代数组可能不会以一致的顺序访问元素。 Therefore it is better to use a for loop with a numeric index (or Array.forEach or the for...of loop) when iterating over arrays where the order of access is important.因此,在迭代访问顺序很重要的数组时,最好使用带有数字索引的 for 循环(或 Array.forEach 或 for...of 循环)。

So.. what have we learned?所以我们学了什么? If order is important to us (often is with arrays), then we need this quirky array in JavaScript, and having a 'length' is rather useful for looping in numerical order.如果顺序对我们很重要(通常是数组),那么我们在 JavaScript 中需要这个古怪的数组,并且具有“长度”对于按数字顺序循环非常有用。

Now think of the alternative: Give your objects an id/order, but then you'd need to loop over your objects for every next id/order (property) once again...现在想想替代方案:给你的对象一个 id/order,但是你需要再次为每个下一个 id/order(属性)循环你的对象......

Edit 3:编辑3:

Someone answered along the lines of:有人回答是这样的:

var a = ['a','b','c'];
a['4'] = 'e';
a[3] = 'd';
alert(a); // returns a,b,c,d,e

Now using the explanation in my answer: what happened is that '4' is coercible to integer 4 and that is in the range [0, 4294967295] making it into a valid array index also called element .现在使用我的答案中的解释:发生的事情是'4'可以强制转换为整数4并且在[0, 4294967295]范围内[0, 4294967295]使其成为有效的数组index也称为element Since var a is an array ( [] ), the array element 4 gets added as array element , not as property (what would have happened if var a was an object ( {} ).由于 var a是一个数组 ( [] ),数组元素4 被添加为数组元素,而不是属性(如果 var a是一个对象( {}会发生什么)。

An example to further outline the difference between array and object:进一步概述数组和对象之间区别的示例:

var a = ['a','b','c'];
a['prop']='d';
alert(a);

see how it returns a,b,c with no 'd' to be seen.看看它如何返回a,b,c而没有 'd' 可见。

Edit 4:编辑4:

You commented: "In that case, an integer index should be handled as a string, as it is a property of the array, which is a special type of JavaScript object."您评论说: “在这种情况下,整数索引应该作为字符串处理,因为它是数组的一个属性,它是一种特殊类型的 JavaScript 对象。” That is wrong in terms of terminology because: (strings representing) integer indexes (between [0, 4294967295]) create array indexes or elements ;这在术语方面是错误的,因为:(表示字符串)整数索引(在 [0, 4294967295] 之间)创建数组indexeselements not properties .不是properties

It's better to say: Both an actual integer and a string representing an integer (both between [0, 4294967295]) is a valid array index (and should conceptually be regarded as integer) and creates/changes array elements (the 'things'/values (only) that get returned when you do arr.join() or arr.concat() for example).最好这样说:实际整数表示整数的string (均在 [0, 4294967295] 之间)都是有效的数组索引(并且在概念上应被视为整数)并创建/更改数组元素(“事物”/例如,当您执行arr.join()arr.concat()时返回的值(仅)。

Everything else creates/changes a property (and should conceptually be regarded as string).其他一切都会创建/更改一个属性(并且在概念上应该被视为字符串)。 What the browser really does, usually shouldn't interest you, noting that the simpler and clearer specified you code, the better chance the browser has to recognize: 'oh, let's optimize this to an actual array under the hood'.浏览器真正做什么,通常不应该让您感兴趣,请注意,您指定的代码越简单和清晰,浏览器就越有可能认识到:“哦,让我们将其优化为引擎盖下的实际数组”。

In JavaScript there are two type of arrays: standard arrays and associative arrays (or an object with properies)在 JavaScript 中有两种类型的数组:标准数组和关联数组(或具有属性的对象)

  • [ ] - standard array - 0 based integer indexes only [ ] - 标准数组 - 仅基于 0 的整数索引
  • { } - associative array - JavaScript objects where keys can be any strings { } - 关联数组 - JavaScript 对象,其中键可以是任何字符串

So ...所以 ...

var arr = [ 0, 1, 2, 3 ];

... is defined as a standard array where indexes can only be integers. ...被定义为一个标准数组,其中索引只能是整数。 When you do arr["something"] since something (which is what you use as index) is not an integer you are basically defining a property to the arr object (everything is object in JavaScript).当您执行 arr["something"] 时,因为 something(您用作索引的内容)不是整数,您基本上是在为 arr 对象定义一个属性(在 JavaScript 中一切都是对象)。 But you are not adding an element to the standard array.但是您没有向标准数组添加元素。

Let's see:让我们来看看:

[1]["0"] === 1 // true

Oh, but that's not conclusive, since the runtime could be coercing "0" to +"0" and +"0" === 0 .哦,但这并不是决定性的,因为运行时可能会将"0"强制转换为+"0"+"0" === 0

[1][false] === undefined // true

Now, +false === 0 , so no, the runtime isn't coercing the value to a number.现在, +false === 0 ,所以不,运行时不会将值强制转换为数字。

var arr = [];
arr.false = "foobar";
arr[false] === "foobar" // true

So actually, the runtime is coercing the value to a string.所以实际上,运行时将值强制转换为字符串。 So yep, it's a hash table lookup (externally).所以是的,它是一个哈希表查找(外部)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM