JavaScript数组:字符串索引项

9

最近我对JavaScript数组索引的本质有了一些认识。在探究过程中,我发现了以下内容(我正在使用Node.js解释模式):

var x=[];
x['a']='a';
console.log(x); // Yields [ a: 'a' ]
console.log(x.length); // yields 0 not 1
x[1]=1;
console.log(x); // Yields [ , 1, a: 'a' ]
console.log(x.length); // Yields 2 not 3 (one for empty 0 space, one for the occupied 1 space)
< p> a: 'a' 真的是看起来的样子 - 嵌入数组的对象属性 - 因此,在数组属性.length中不计算吗?


这句话的意思是,一个包含对象属性的数组是否会在计算数组长度时将对象属性也计算在内。

不错。我甚至不知道这是可能的。但是,数组只是一个对象,所以这很有意义。 - iambriansreed
3个回答

13
在JavaScript中,数组只是带有一些特殊属性的对象,例如自动的length属性以及附加的一些方法(例如sortpopjoin等)。实际上,数组的长度仅存储可以用32位正整数表示的属性名称的元素数量,因此a不会计入您的数组。
由于数组始终自动定义每个编号元素,直到具有正32位int属性名称的最高元素,这实际上意味着长度属性存储了比最高32位整数元素名称更高1。感谢@ Felix Kling 在评论中纠正了我对此的理解。
添加诸如a之类的属性并不是完全被禁止的,但您应该注意它们,因为读取代码时可能会产生混淆。
在遍历数组中的所有编号元素方面存在差异:
要遍历所有编号元素:
for (var i=0; i<myArray.length; i++) {
    //do something
}

遍历每个非内置属性:

for (var i in myArray) {
    //do something
}

请注意,这个循环也会包含任何从Array.prototype包含的不是内置方法的东西。因此,如果您添加了Array.prototype.sum = function() {/*...*/};,它也将被循环遍历。

要找出您正在使用的对象是否确实是数组而不仅仅是对象,您可以执行以下测试:

if (Object.prototype.toString.call(myObject) === '[object Array]') {
    //myObject is an array
} else if (typeof myObject === 'object') {
    //myObject is some other kind of object
}

看看@artem的评论myObject instanceof Array可能并不总是能正确地工作。


3
"only counts the numerable values in the array" 的意思是只计算数组中可数的值,不包括数组元素本身。而 length 属性始终为 1 加上最大属性名(可以转换为32位整数)的值。 - Felix Kling
@FelixKling 感谢您的纠正。我已经在我的帖子中做出了相应的更改。 - Joeytje50
1
myObject instanceof Array 对于某些数组可能会返回 false(不确定 node.js,我记得有一种情况是当数组从另一个 iframe 传递时,显然在某些浏览器中每个窗口都有自己的 Array 构造函数实例)。在这种情况下,像 jQuery 所做的那样始终是一个好主意:Object.prototype.toString.call(myObject) === '[object Array]' - artem
1
您需要将“可以用32位整数表示”更正为“可以用32位整数(或零)表示”。负值不计入length属性。 - Arturo Torres Sánchez
@artem 由于这是node.js,它在单个进程中运行,并且Array构造函数始终相同。即使不是这样,也应该使用Array.isArray进行检查。 - LJᛃ

3

没错,这很好地说明了你创建的新数组实际上只是一种特殊类型的对象。事实上,typeof [] 的值为 'object'!在它上面设置一个命名属性(可以更清晰地写成 x.a = 'a')实际上是在对象包装器中设置一个新的属性用于“真实”数组中的编号属性(实际上就是数字下标)。它们不影响长度属性的原因与 Array.isArray 方法相同。


数组周围没有“对象包装器”。 数组本身就是对象。 - user663031
是的,从技术上讲这是正确的。但如果你正在使用一个数组,那么这是因为你特别想要有序列表部分,所以把它看作与“Object”不同(并具有不同的用途)是有意义的。 “Number”类型也从技术上讲是一个对象(即它继承自“Object.prototype”),但把它看作一个对象真的没有什么帮助。我的术语“对象包装器”只是一个抽象概念,用于区分数组和普通对象。 - OverlappingElvis

2

是的,没错。这是因为在JavaScript中几乎所有东西都是对象,包括数组和函数,这意味着你可以添加自己的任意字符串属性。这并不意味着你应该这样做。

数组([])应该使用非负整数进行索引。对象({})应该使用字符串进行“索引”。


JavaScript 中的几乎所有东西都是对象。我想你的意思是“JavaScript 中的所有值都是对象,除了原始类型,包括数字、字符串、null 和 undefined”。为什么不应该在数组上放置字符串属性?这取决于情况,这是一种完全有效的技术手段。 - user663031

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接