JavaScript数组中的负索引是否应有助于数组长度?


84

在JavaScript中,我定义了这样的数组

var arr = [1,2,3];

我也可以

arr[-1] = 4;

现在如果我这样做

arr = undefined;

我也丢失了对arr [-1]值的引用。

因此从逻辑上来说,似乎arr [-1]也是arr的一部分。

但是当我执行以下操作时(未将arr设置为undefined)

arr.length;

返回3而不是4 ;

因此,我的观点是,如果数组可以与负索引一起使用,则这些负索引也应该是其长度的一部分**。我不知道可能是我错了,还是可能缺少一些关于数组的概念。


为什么要使用负索引?除非我缺少任何东西,否则我看不到重点。
elclanrs 2012年

11
您也可以编写arr[1.5] = 1,并且也不会影响长度。语言规范非常清楚影响长度的因素。您可能不喜欢它,但必须忍受它。要么设计自己的竞争性语言,然后说服人们改用它。
Raymond Chen

1
@DigvijayYadav:可以像JavaScript中的许多其他事物一样被视为缺陷或功能。这是因为数组的行为有点像对象,您也可以使用字符串,var a = []; a['foo'] = 'baz'但这并不意味着您应该这样做。显然违反所有惯例。
elclanrs 2012年

2
伙计们,这里的主要要点是数组是对象。没有区别。这就是这种行为的原因,即使您不喜欢它也是完全故意的。只需等到了解到所有数字都被表示为浮点,甚至整数即可。JavaScript是一种奇怪的语言……
Tony R

2
您可以在规范中找到定义数组元素设置的算法的详细信息:es5.github.com/#x15.4.5.1。(尤其是步骤4),此处定义了“数组索引”:es5.github.com /#x15.4
Felix Kling

Answers:


109

因此从逻辑上来说,似乎arr [-1]也是arr的一部分。

是的,但不是您认为的那样。

您可以为数组分配任意属性(就像JavaScript中的任何其他Object一样),这是在数组上“索引”-1并分配值时要执行的操作。由于这不是数组的成员,而只是一个任意属性,因此不应期望length考虑该属性。

换句话说,以下代码执行相同的操作:

var arr = [1, 2, 3];

​arr.cookies = 4;

alert(arr.length) // 3;

31
好的,这意味着负索引实际上并不像真实索引那样工作。
me_digvijay 2012年

4
是的,它们只是数组上的任意属性。
安德鲁·惠特克

但是,如果我想使用正索引作为属性而不是索引,并且我不希望它们对数组长度有所贡献怎么办?
me_digvijay 2012年

7
然后,不要使用数组,而要使用普通对象。但是在那种情况下,您将失去内置length属性。
Andrew Whitaker

2
@Digvijay:控制台仅显示带有正整数名称的属性,因为(我假设)它只是执行for0到的普通循环.length。不要console.dir(arr)看到完整的对象。同样,使用括号表示法访问数组也不是数组的特征,而是对象(var obj = {foo: 'bar'}; obj.foo or obj['foo'])的固有特征。
Felix Kling 2012年

25

length属性将返回比分配的最高“索引”高一个数字,其中数组“索引”是大于或等于零的整数。请注意,JS允许“稀疏”数组:

var someArray = [];
someArray[10] = "whatever";
console.log(someArray.length); // "11"

当然,如果没有元素,那length就是0。另请注意,length如果您使用,则不会更新delete用于删除最高元素。

但是数组是对象,因此可以为属性分配其他任意属性名称,包括负数或分数:

someArray[-1] = "A property";
someArray[3.1415] = "Vaguely Pi";
someArray["test"] = "Whatever";

请注意,即使您提供如下数字,JS也会在后台将属性名称转换为字符串 -1。(正整数索引也将变成字符串。)

数组方法(例如.pop().slice()等)仅适用于零或更高的整数“索引”,不适用于其他属性,因此length在这一点上是一致的。


谢谢您的回答,对您有所帮助。我还尝试了带有负索引的splice方法,发现如果我使用-1,它将转到数组的最后一个元素,对于-2,它将转到第二个最后一个元素,依此类推。
me_digvijay 2012年

+1。长度不能告诉您有多少个元素或有多少个属性。这只是指数最高+ 1,例如教var a = new Array(9)a具有9的长度,根本没有元素。
RobG 2012年

1
@DigvijayYadav-是的,该Array.slice()方法应该做到这一点。该String.slice()方法执行相同的操作。
nnnnnn 2012年

7

请注意,当您使用位置(或0)索引时,值将放置在数组中:

var array = [];

array[0] = "Foo";
array[1] = "Bar";

// Result: ["Foo", "Bar"]
// Length: 2

当您添加非索引值(不是0-9 +)时,情况并非如此:

var array = [];

array[0]  = "Foo";
array[1]  = "Bar";
array[-1] = "Fizzbuzz"; // Not a proper array index - kill it

// Result: ["Foo", "Bar"]
// Length: 2

仅当您按规则进行操作时,才将值放置在数组中。如果您不这样做,它们将不会被接受。但是,它们在Array对象本身上就被接受,JavaScript中的几乎所有情况都是如此。即使["Foo", "Bar"]是数组中唯一的值,我们仍然可以访问"Fizzbuzz"

array[-1]; // "Fizzbuzz"

但请再次注意,这不是数组值的一部分,因为其“索引”无效。相反,它只是作为另一个成员添加到阵列中。我们可以以相同的方式访问其他数组成员:

array["pop"]; // function pop() { [native code] }

请注意,这里我们正在访问pop数组上的方法,这告诉我们其中包含本机代码。我们不是使用键“ pop”访问任何数组值,而是访问数组对象本身的成员。我们可以通过遍历对象的公共成员来进一步确认这一点:

for (var prop in array) 
    console.log(prop, array[prop]);

其中吐出以下内容:

 0 Foo
 1 Bar
-1 Fizzbuzz

如此反复,这是对象,但它不是阵列

很棒的问题!使我确定要进行两次拍摄。


1
+1甚至规范都指出,具有正数值属性名称的属性称为数组的元素(而其他任何属性都不是):es5.github.com/#x15.4
Felix Kling 2012年

结果不是:["Foo", "Bar"]但是在这里["Foo", "Bar", -1: "Fizzbuzz"]检查小提琴。就像您写的一样,它变成了具有键-1的属性,但在输出中绝对可见。否则,您的答案会很完整。如果您更改,我将再次投票,我的投票很严厉,但无法删除。时间到期了。
威尔特

5

如果您确实希望负索引和其他索引包含在长度中,请自己创建功能:

function getExtendedArray() {
    var a = [];

    Object.defineProperty(a, 'totalLength', { 
        get : function() { return Object.keys(a).length; }
    });

    return a;
}

然后例如:

var myArray = getExtendedArray();
console.log(myArray.totalLength); // 0
myArray.push("asdf");
myArray[-2] = "some string";
myArray[1.7777] = "other string";
console.log(myArray.totalLength); // 3

而且,如果您希望长度仅包括编号索引/属性,则可以为该键添加if语句-if(Number(key) != NaN) length++;如果要过滤掉浮点数,请添加&& key % 1 == 0条件
Bonnev

4

数组只是JS中的一种特殊对象。有一个特殊的语法,这很好,因为它使我们可以有效地使用它们。想象这样写一个数组文字将是多么乏味:

const array = {0: 'foo', 1: 'bar', 2: 'baz'} //etc...

但是它们仍然是对象。因此,如果您这样做:

const myArray = [42, 'foo', 'bar', 'baz'];

myArray[-1] = 'I am not here';
myArray['whatever'] = 'Hello World';

这些非整数属性将附加到对象(数组是哪个),但是某些本机方法无法访问这些属性,例如 Array.length

您可以假定本机的“长度”方法是一种吸气剂,它计算所有属性(索引属性,而值是实际值)可转换为正整数或零的)进行。像这样的伪实现:

根据规范,本机length方法(作为getterexampleArray.length))将检查所有小于2 32的“负整数”的”键,获取最大的键并返回其数值+ 1。

作为设置器exampleArray.length = 42),如果给定长度大于非负整数键的实际数量,它将为“丢失”索引创建一些空位,或者删除所有不大于整数的非负整数键(及其值)。给定的长度。

伪实现:

const myArray = {
  '-1': 'I am not here', // I have to use apostrophes to avoid syntax error
  0: 42,
  1: 'foo',
  2: 'bar',
  3: 'baz',
  whatever: 'Hello World',

  get myLength() {
    let highestIndex = -1;
    for (let key in this) {
      let toInt = parseInt(key, 10);
      if (!Number.isNaN(toInt) && toInt > -1) {
        // If you're not an integer, that's not your business! Back off!
        if (toInt > highestIndex) highestIndex = toInt;
      }
    }
    return highestIndex + 1;
  },
  set myLength(newLength) {
    /* This setter would either:
      1) create some empty slots for 'missing' indices if the given length is greater than the actual number of non-negative-integer keys
      2) delete all non-negative-integer keys (and their values) which are not greater then the given length.
    */
  }
}

console.log(myArray.myLength); // 4

myArray[9] = 'foobar';

console.log(myArray.myLength); // 10


3

JavaScript中的数组实际上是对象。它们只是从Array构造函数原型化而来。

数组索引实际上是哈希图中的键,并且所有键都转换为字符串。您可以创建任何键(即“ -1”),但是Array上的方法被定制为像数组一样工作。因此length,对象的大小不是,而是仅保证大于最大整数索引。同样,打印arr将仅列出整数键> = 0的值。

更多信息在这里。


究竟。否则我们可能会开始怀疑为什么array ['foo']不是有效的索引-javascript允许您执行此操作,但并不意味着数组(及其元素)的定义会从一种语言变为另一种语言。按预期工作。
tw airball

实际上,“数组”在JS中不存在。但是“关联数组”(也称为“对象”)是JS的核心。因此,制作一个模仿常规数组的Array对象并不是一个巨大的飞跃。
Tony R

1
数组和普通对象之间有一个非常特殊的区别:自调整长度属性。
RobG 2012年

1

根据MDN:

length属性的值是一个带正号的整数,其值小于32的幂(232)。

在Javascript中,您可以在创建的任何对象上设置属性。

var array = new Array();
array = [1,2,3];
array["boom"] = "pow";

以同样的方式,当您设置负索引时,会将其作为属性存储在数组中,而不是索引的一部分。

array[-1] = "property does not add to array length";

这就是为什么长度不反映长度,而for..in循环显示长度的原因。


-1

当您使用非正数索引或非数字索引时,该数组的行为就像一个具有键值对的关联数组。

要遍历数组,可以使用

for(var index in myArray) {
  document.write( index + " : " + myArray[index] + "<br />");
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.