【问题标题】:Should negative indexes in JavaScript arrays contribute to array length?JavaScript 数组中的负索引是否会影响数组长度?
【发布时间】:2012-11-17 02:25:20
【问题描述】:

在javascript中我定义了一个这样的数组

var arr = [1,2,3];

我也可以

arr[-1] = 4;

如果我这样做了

arr = undefined;

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

所以对我来说逻辑上似乎 arr[-1] 也是 arr 的一部分。

但是当我执行以下操作时(没有将 arr 设置为未定义)

arr.length;

它返回 3 而不是 4;

所以我的观点是如果数组可以与负索引一起使用,这些负索引也应该是它们长度的一部分**。 我不知道可能是我错了,或者我可能遗漏了一些关于数组的概念。

【问题讨论】:

  • 为什么要使用负索引?这很令人困惑。除非我遗漏了什么,否则我看不出重点。
  • 你也可以写arr[1.5] = 1,这也不影响长度。语言规范非常清楚影响长度的因素。你可能不喜欢它,但你必须忍受它。要么设计自己的竞争语言并说服人们改用它。
  • @DigvijayYadav:与 JavaScript 中的许多其他东西一样,可以被视为缺陷或功能。这是因为数组的行为有点像对象,您也可以使用字符串var a = []; a['foo'] = 'baz',但这并不意味着您应该这样做;这显然违反了所有惯例。
  • 伙计们,这里的重点是数组是对象。没有区别。这就是这种行为的原因,即使你不喜欢它也完全是故意的。等到您知道所有数字都表示为浮点数,甚至整数。 JavaScript 是一种奇怪的语言...
  • 您可以在规范中找到定义数组元素设置的算法的详细信息:es5.github.com/#x15.4.5.1.(especially step 4) 和“数组索引”在这里定义:es5.github.com/#x15.4

标签: javascript arrays


【解决方案1】:

Array只是JS中一种特殊的Object。有一种特殊的语法很好,因为它允许我们有效地使用它们。想象一下这样写一个数组字面量是多么乏味:

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' 方法是一个统计所有属性的 getter(索引属性,而值是实际的)可转换为正整数或零。像这样的伪实现:

由于specs,原生length 方法——作为getter (exampleArray.length)——将检查所有“小于232的非负整数”键,取最大的一个并返回其数值 + 1。

作为 setter (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

【讨论】:

    【解决方案2】:

    所以对我来说逻辑上似乎 arr[-1] 也是 arr 的一部分。

    是的,但不是你想象的那样。

    您可以将任意属性分配给数组(就像 JavaScript 中的任何其他对象一样),这就是您在 -1 处“索引”数组并分配值时所做的事情。由于这不是数组的成员,只是一个任意属性,因此您不应期望 length 考虑该属性。

    换句话说,下面的代码做同样的事情:

    ​var arr = [1, 2, 3];
    
    ​arr.cookies = 4;
    
    alert(arr.length) // 3;
    

    【讨论】:

    • 好的,这意味着负索引实际上并不像真正的索引。
    • 对,它们只是数组上的任意属性。
    • 但是如果我想使用正索引作为属性而不是索引,并且我不希望它们对数组长度有贡献呢?
    • 那么不要使用数组,使用普通的 ol' 对象。但在这种情况下,您将失去内置的 length 属性。
    • @Digvijay:控制台只显示具有正整数名称的属性,因为(我假设)它只是执行从0.length 的普通for 循环。执行console.dir(arr) 以查看完整对象。此外,使用括号表示法访问数组不是数组的特性,而是对象的固有特性 (var obj = {foo: 'bar'}; obj.foo or obj['foo'])。
    【解决方案3】:

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

    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
    

    【讨论】:

    • 如果您希望长度仅包含 numbered 索引/属性,您可以为键添加 if 语句 - if(Number(key) != NaN) length++; 如果您想过滤掉浮点数那些在条件中添加&& key % 1 == 0
    【解决方案4】:

    根据 MDN:

    length 属性的值是一个带正号且小于 2 的 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 循环显示它。

    【讨论】:

      【解决方案5】:

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

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

      More info here.

      【讨论】:

      • 完全正确。或者我们可能会开始想知道为什么 array['foo'] 不是一个有效的索引 - javascript 允许你这样做,但并不意味着数组(及其元素)的定义从一种语言变为另一种语言。按预期工作。
      • 事实上“数组”在 JS 中并不存在。但是“关联数组”也称为“对象”是 JS 的核心。因此,制作一个模仿常规数组的 Array 对象并不是一个巨大的飞跃。
      • 数组和普通对象之间有一个非常特殊的区别:自调整长度属性。
      【解决方案6】:

      请注意,当您使用位置(或 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
      

      同样,它是在 on object,但它不是 in array

      真棒的问题!导致我肯定做了双重检查。

      【讨论】:

      • +1 甚至规范声明具有正数属性名称的属性称为数组的元素(任何其他属性都不是):es5.github.com/#x15.4。跨度>
      • 结果不是:["Foo", "Bar"],而是["Foo", "Bar", -1: "Fizzbuzz"] 检查fiddle here。就像您写的那样,它成为带有键 -1 的属性,但它在输出中绝对可见。否则,您的答案很好且完整。如果你改变我会再次投票,我的反对票是苛刻的,但不能删除它;时间已过。
      【解决方案7】:

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

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

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

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

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

      请注意,即使您提供像-1 这样的数字,JS 也会在后台将属性名称转换为字符串。 (就此而言,正整数索引也变成了字符串。)

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

      【讨论】:

      • 感谢您的回答帮了大忙。我还尝试了带有负索引的拼接方法,我发现如果我使用 -1 它会转到数组的最后一个元素,因为 -2 会转到倒数第二个元素,依此类推。
      • +1。长度不会告诉你有多少元素,或者有多少属性。这只是最高指数+ 1。例如给定var a = new Array(9)a 的长度为 9,根本没有元素。
      • @DigvijayYadav - 是的,Array.slice() method 应该这样做。 String.slice() method 做同样的事情。
      【解决方案8】:

      当您使用非正数或非数字索引时,数组表现为具有键值对的关联数组。

      要遍历数组,您可以使用

      for(var index in myArray) {
        document.write( index + " : " + myArray[index] + "<br />");
      }
      

      【讨论】:

        猜你喜欢
        • 2011-03-29
        • 2014-03-22
        • 2010-09-13
        • 2013-07-07
        • 2019-03-08
        • 1970-01-01
        • 1970-01-01
        • 2014-01-21
        • 1970-01-01
        相关资源
        最近更新 更多