【问题标题】:Array.length gives incorrect lengthArray.length 给出不正确的长度
【发布时间】:2016-06-15 09:42:11
【问题描述】:

如果我有一个将对象作为索引值的数组,例如:

var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
alert(a.length); // outputs 91

我找到了获得实际长度的解决方法:

function getLength(arr) {
    return Object.keys(arr).length;
}
var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
alert(getLength(a));

但是,当对象存储在随机索引处时,为什么 JS 会给出不正确的长度?它只是在数组中找到的最大索引上加 1。和上面的例子一样,90 是最大的索引。它只是加 1 并给出 91 作为输出。 Demonstration

【问题讨论】:

  • 您描述的行为是官方规范中定义的。 ecma-international.org/ecma-262/5.1/#sec-15.4.5.2
  • 问题是:为什么你对.length有错误的假设?想象一下.length 返回了3,而您尝试使用for 循环来获取值。你永远不会得到价值......
  • @CoryDanielson 谢谢。不知道有这样的官方规范存在!我现在总是会参考它。
  • @FelixKling 听不懂你在说什么。我不擅长英语:P 我需要这样的索引,因为它们在我的脚本中起到了很大的作用。我认为找到.length 的修复程序会很好。其他一切正常。
  • @CoryDanielson——它在§22.1.4 Properties of Array Instances下。

标签: javascript arrays


【解决方案1】:

这是因为length 为您提供了数组中的下一个index

DOCS

数组长度

如果传递给 Array 构造函数的唯一参数是 0 到 2^32-1(含)之间的整数,则返回一个长度设置为该数字的新 JavaScript 数组。

ECMA Specifications

由于您没有在 21、90、13 以外的其他键中插入任何元素,因此所有剩余索引都包含 undefinedDEMO

获取数组中元素的实际数量:

var a = [];
a[21] = {};
a[90] = {};
a[13] = {};

var len = 0;

for (var i = 0; i < a.length; i++) {
  if (a[i] !== undefined) {
    len++;
  }
}
document.write(len);

短版

var a = [];
a[21] = {};
a[90] = {};
a[13] = {};


for (var i = 0, len = 0; i < a.length; i++, a[i] !== undefined && len++);


document.write(len);

DEMO

编辑

如果数组包含大量元素,循环获取其长度不是最佳选择。

正如您在问题中提到的那样,考虑到您没有在该数组上添加任何属性,Object.keys(arr).length 是这种情况下的最佳解决方案。否则,length 将不是您所期望的。(感谢@RobG)

【讨论】:

  • 谢谢。我现在可以理解了。但是,如果我有a[2020] = {} 怎么办?我认为循环遍历所有数组并不是很好。我现在会坚持我的方法,直到其他人提出更好的解决方案。 IDK Object.keys 有多快,但至少会比循环快。
  • Object.keys 将返回所有自己的属性,可能不都是索引。
  • @RobG 你能解释一下,这将如何影响这种阵列场景
  • @Tushar——var a = []; a.foo = 'foo';。现在a.length 为 0 但Object.keys(a).length 为 1。
  • @RobG 感谢您的解释。但是,我认为 OP 不会这样做。只是一个想法
【解决方案2】:

JavaScript 中的数组是一个简单的从零开始的结构。 array.length 返回n + 1,其中n 是数组中的最大索引。

这就是它的工作原理 - 当您分配第 90 个元素并且此数组的长度小于 90 时,它将数组扩展为 90 并设置第 90 个元素的值。所有缺失值都被解释为null

如果您尝试以下代码:

var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
console.log(JSON.stringify(a));

您将获得以下 JSON:

[null,null,null,null,null,null,null,null,null,null,null,null,null,{},null,null,null,null,null,null,null,{},空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空,空, null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,{}]

此外,array.length 不是只读值。
如果您设置的length小于,则数组将被调整大小:

 var arr = [1,2,3,4,5];
 arr.length = 3;
 console.log(JSON.stringify(arr));
 // [1,2,3]

如果您设置的length比当前值更多,那么数组也会被扩展:

 var arr = [1,2,3];
 arr.length = 5;
 console.log(JSON.stringify(arr));
 // [1,2,3,null,null]

如果你需要分配这样的值,你可以使用 JS 对象。
您可以将它们用作关联数组并分配任何键值对。

var a = {};
a[21] = 'a';
a[90] = 'b';
a[13] = 'c';
a['stringkey'] = 'd';
a.stringparam = 'e'; // btw, a['stringkey'] and a.stringkey is the same

console.log(JSON.stringify(a)); 
// returns {"13":"c","21":"a","90":"b","stringkey":"d","stringparam":"e"}

console.log(Object.keys(a).length);
// returns 5

【讨论】:

  • 我明白了。您建议使用 JS 对象,但我如何模拟与数组相同的行为?只需提供索引并获得一个值。
  • 顺便说一句,“缺失”值不会“解释为 null”(并且脚本控制台不会以这种方式报告它们)。访问缺少的成员会返回 undefined,这与访问任何对象的不存在的属性完全相同。成员只有在被分配了该值的情况下才会具有值 null,在这种情况下它不是“丢失”。 ;-)
【解决方案3】:

因为这就是 ECMAScript spec 中描述的 Array.length 的行为。

此 Array 对象的长度属性是一个数据属性,其值在数值上始终大于名称为数组索引的每个可删除属性的名称。

所以 Array.length 总是最后一项的索引 + 1。

【讨论】:

    【解决方案4】:

    这是因为你有一个 [90] 作为最大的索引,所以索引从 0 到 90 的长度变成了 91。

    如果您没有传递 a[80] 等值,javascript 会将它们存储为孔,例如 [1, , 3, , ,90],其中使用逗号表示数组中的孔。

    如果您尝试访问这些值,您将获得undefined

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-14
      • 1970-01-01
      • 2021-06-24
      • 1970-01-01
      • 2021-05-19
      • 2015-10-01
      • 1970-01-01
      相关资源
      最近更新 更多