【问题标题】:What does the following Array initialization syntax actually create? [duplicate]以下 Array 初始化语法实际上创建了什么? [复制]
【发布时间】:2017-12-08 01:20:14
【问题描述】:

以下实际创造了什么?

我问是因为它不能用 Array#foreach 枚举,并且它在 Chrome 控制台中表示(误导?)为 [undefined x 10]

const arr = new Array(10);

【问题讨论】:

  • console.log(arr);。它将创建一个大小为 10 且值未定义的数组。 jsfiddle.net/o9f25Ljz
  • 不,我认为不会。请尝试一下。
  • 运行时将已显式赋值的数组元素与未显式赋值的数组元素区分开来。
  • 不能枚举,因为数组不包含任何元素。你很困惑,因为看起来有 10 个 undefined 值,但事实并非如此,什么都没有,“holes”
  • const 变量无法重新分配。同时,可以更改对象内容。

标签: javascript


【解决方案1】:

Array 对象具有三种不同的可能构造函数操作:

  1. new Array()
  2. new Array(<multiple arguments)
  3. new Array(<one argument>)(由于其性质而放在最后 - 进一步了解)

构造函数的行为取决于提供给该构造函数的参数。在构造函数操作期间,所有这三个操作都使用ArrayCreate。从链接:

带有参数长度(一个正整数)和可选参数 proto 的抽象操作 ArrayCreate 用于指定创建新的 Array 奇异对象。

此函数接受两个参数:len(长度)和proto,用于创建数组对象。 (我不会在这里更深入,因为它会更复杂)。在这种情况下,len 参数更为重要。当我们单独查看构造函数时,您有

new Array()

这里,ArrayCreate(0, proto) 构造了一个对象,因此.length = 0 是一个空数组。

new Array(<multiple arguments>)

这里,一个对象是由ArrayCreate(#of args, proto)构造的。如果您提供 3 个参数,那么您将获得一个带有 .length = 3 的数组。然后使用提供的参数填充此数组。你给出5个论据吗?你得到.length = 5。所以.length 属性取决于参数的数量。

new Array(<one argument>)

这个是完全不同的。它有两种情况。在初始化时,使用ArrayCreate(0, proto) 创建一个数组对象,就像您使用new Array() 一样。我们仍然有一个论点可以使用。

如果参数是 非数字 值,则效果与第 2 点相同。此参数被添加到构造对象中(如 .push() 操作。所以 new Array('hello')new Array('10') 为您提供带有.length = 1 的数组对象

现在,当你给出一个 numeric 参数 new Array(10) 时,情况就大不相同了,它在 Chrome 中给出了 [undefined x 10]。为什么?因为当参数为数字(且有效)时,将调用此函数:Set(O,P,V,Throw)。这个“神奇”函数将对象O 的属性P 设置为值V。 (Throw 是一个标志,表示“throw error or not?”问题)。

如果参数是一个长度,set(<constructed array>, 'length', <argument>, true) 正在被执行。因此.length 属性返回参数值,而内存中没有保留任何内容。最终结果:你有一个带有假的.length 属性的对象......这样做的副作用是使用.push 将一个元素添加到第10 个索引(如果.length 是10)。现在您有一个包含 11 个元素的数组:10 个“空”插槽和一个您刚刚推送的元素。

那么浏览器如何处理带有“假”.length 属性的数组呢?他们无法正确地迭代它。因此它们提供了一种故障保护方式:显示“有 10 个元素但为空”,就像 chrome 使用 [undefined x 10] 或 firefox:Array [ <10 empty slots>]

如果您问我为什么在规范中将其标准化,那么我会回答“我不知道”。这是 ECMAScript 中标准化的最奇怪的决定之一(在我遇到的其他决定中)。

【讨论】:

    【解决方案2】:

    以下两个sn-ps是等价的:

    const arr = new Array(10)
    

    const arr = [,,,,,,,,,,]
    

    可以在这里演示:

    const arr1 = new Array(10)
    const arr2 = [,,,,,,,,,,]
    
    let empty1 = true
    
    for (let index = 0; empty1 && index < arr1.length; index++) {
      if (arr1.hasOwnProperty(index)) {
        empty1 = false
      }
    }
    
    let empty2 = true
    
    for (let index = 0; empty1 && index < arr2.length; index++) {
      if (arr2.hasOwnProperty(index)) {
        empty2 = false
      }
    }
    
    console.log(empty1)
    console.log(empty2)

    这意味着对于来自[0, length - 1] 的所有索引,它们实际上并不存在于数组中,因为arr.hasOwnProperty(index)false。这与此示例不同:

    const arr = new Array(10).fill()
    

    在这里演示:

    const arr = new Array(10).fill()
    
    console.log(arr.every((_, index) => arr.hasOwnProperty(index)))

    因此,如果您想用undefined 初始化构造 数组中的每个索引,只需调用.fill() 而不带任何参数。

    【讨论】:

      【解决方案3】:

      Array 构造函数在提供单个整数值时会创建一个 Array 实例,并将 length 自己的属性设置为该值。

      像浏览器这样的宿主环境可以选择渲染它。 V8 选择了[undefined x 10],这是一种误导,因为这意味着在有序序列中有十个预定义的undefined 值实例。

      事实并非如此。

      Array#foreachArray#map 使用可枚举整数索引和 length 自身属性值来枚举数组的值。

      因为这个特定的Array 构造函数语法不会初始化Array 对象上的任何整数属性,所以它们无法枚举以这种方式初始化的Arrays。

      我只能假设此语法仅存在于启用依赖于 Array 实例的 length 自身属性的代码模式,而无需初始化任何键或值。

      【讨论】:

      • 来自 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… : forEach() executes the provided callback once for each element present in the array in ascending order. It is not invoked for index properties that have been deleted or are uninitialized (i.e. on sparse arrays). - 如果你想添加到你的帖子中
      • 您声明构造函数没有初始化任何整数属性,但是为什么arr[0] 会输出一些东西? -> jsfiddle.net/o9f25Ljz/1
      • 不存在的属性的值为undefined
      • 如果我做 console.log(newArr[0]); 什么都不输出(没有初始化 newArr) - 不是 undefined
      猜你喜欢
      • 2014-07-14
      • 2015-03-24
      • 2013-01-10
      • 1970-01-01
      • 1970-01-01
      • 2021-04-12
      • 1970-01-01
      • 2021-12-09
      • 2010-12-09
      相关资源
      最近更新 更多