Array(len) 初始值设定项中的未定义值

IT技术 javascript arrays undefined
2021-01-25 11:19:53

考虑:

var a = Array(3);
var b = [undefined,undefined,undefined];

a.mapb.map产生不同结果的原因是什么

a.map(function(){  return 0;  });  //produces -> [undefined,undefined,undefined]
b.map(function(){  return 0;  });  //produces -> [0,0,0]
5个回答

数组构造函数创建一个具有给定长度的数组。它并不会创建密钥。Array.prototype.map的回调函数只对列表中的元素执行。
也就是说,与键(整数) 0 ≤ i < length相关联的所有值

  • Array(3)有零个键,因此.map永远不会触发 的回调。
  • [void 0, void 0, void 0] 有三个键,为其执行回调函数。

    Array(3).hasOwnProperty(0);                 // false
    [void 0, void 0, void 0].hasOwnProperty(0); // true
    

MDN 上提到了规范及其polyfill在第 47 行,if (k in O) {显示不存在的键不会被回调函数处理。

你没有说“数字”,非数字键将被地图忽略,因此从结果中剥离,这不是一个头发分裂的区别。而且数字也是不对的,所有浮点和负整数的数字键(呃……都是字符串,真的……)也会遇到非数字的命运。
2021-03-13 11:19:53
@RobW 我们可以假设,这Array(3)并没有真正为存储3元素分配内存,哪些值是undefined,它只是将length属性设置3,就这样吗?
2021-03-30 11:19:53
实际上,map 不应该遍历键,而是遍历值序列 0..array.length-1
2021-04-01 11:19:53
@工程师是的。验证很简单:打开 Chrome 的 devtools (Ctrl+Shift+J) 和内置任务管理器 (Shift+Esc),然后输入window.x=Array(1e9);. 您没有看到内存使用量的显着增加。ArrayBuffer,另一方面:x=new ArrayBuffer(1e9);显示内存消耗增加。
2021-04-03 11:19:53
@panda-34 在 0 和 n 之间跳过键类似于在数字键上走动。但你是对的,严格来说,我应该这么说;)
2021-04-04 11:19:53

来自MDN

仅对已分配值的数组索引调用回调;不会为已删除或从未分配值的索引调用它。

对于数组a,您已经实例化了一个长度为 3 的数组,但尚未分配任何值。map 函数找不到具有指定值的元素,因此它不会生成新数组。

对于 array b,您已经实例化了一个包含 3 个元素的数组,每个元素的值为undefinedmap 函数找到 3 个具有指定值的元素,并在新数组中为每个元素返回 '0' 作为新值。

map 只迭代现有属性,而不是空索引。

因此,如果您希望它工作,您必须先填充数组。

有多种方法可以做到这一点,例如:

  • .fill(), 在 ES6 中引入

    console.log(new Array(3).fill().map(function(){ return 0; }));

  • 打电话concatapply

    var arr = [].concat.apply([], new Array(3));
    console.log(arr.map(function(){ return 0; }));

  • 一个旧for循环。

    var arr = new Array(3);
    for(var i=0; i<arr.length; ++i) arr[i] = 1; /* whatever */
    console.log(arr.map(function(){ return 0; }));

  • 使用最有效的方法中的一些想法来创建零填充的 JavaScript 数组?

  • 等等。

a是一个没有元素的空数组,因此 map 函数生成没有元素的空数组(根据规范,map 仅在 [[HasProperty]] 为真时才生成结果。) b是一个包含三个元素的数组,因此 map 生成一个数组三个要素。

构造的数组可枚举但为空

Array(len)创建一个数组并相应地设置其长度,只有它的长度是“可枚举的”,而不是包含的值。所以,你不能阵图Array(100).map(/* nope */)-它不是一个“真正的数组”然而,它是尽管有正确的长度就是空的。

仅对已分配值(包括未定义)的数组索引调用回调,

array不包含任何值; 甚至不undefined

对于数组中缺失的元素(即从未设置、已删除或从未赋值的索引),不会调用它。

要填充数组,您需要以某种方式对其进行迭代……例如:[...Array(100)]Array.from(Array(100))

我想这个初始化的目的是优化内存分配……数组中没有任何东西。MDN 说“空arrayLength对象”可能会产生误导,因为尝试访问任何“空项目”只会返回 undefined ……但我们知道它们不是真的undefined因为map失败,因此我们可以确认它是一个真正的空数组。

等价的构造函数

这个例子并不试图反映规范,而是为了说明为什么array返回的 fromArray还不能被迭代

function * (length) {
  const arr = [];
  Object.defineProperty(arr, 'length', { value: length });
  // Equivalent, but invokes assignment trap and mutates array:
  // arr.length = length;
  Object.defineProperty(arr, Symbol.iterator, {
    value() {
      let i = 0;
      return {
        next() {
          return {
            value: undefined, // (Not omitted for illustration)
            done: i++ == length
          };
        }
      }
    }
  })
  return arr;
}

值得指出的是,尽管undefined在生成器 value 属性中提供了一个值,但它不会将其识别为值,因此数组为空

Array(len) 规格

https://www.ecma-international.org/ecma-262/6.0/#sec-array-len

Array (len) 当且仅当使用恰好一个参数调用 Array 构造函数时,此描述才适用。

1)numberOfArgs设为传递给此函数调用的参数数量。

2)断言:numberOfArgs= 1。

3) 如果 NewTarget 是undefined,则newTarget设为活动函数对象,否则newTarget设为NewTarget

4)我们protoGetPrototypeFromConstructor(newTarget, "%ArrayPrototype%")

5)ReturnIfAbrupt(proto)

6) 令数组为ArrayCreate(0, proto)

7) 如果 Type( len) 不是 Number,则

a)设defineStatusCreateDataProperty(array, "0", len)

b) 断言:defineStatus是真的。

c)intLen设为 1。

8) 否则,a)intLen设为 ToUint32( len)。b) 如果intLenlen,则抛出 RangeError 异常。

9) setStatus设为 Set(array, "length", intLen, true)。

10) Assert:setStatus不是突然的完成。

11) 返回数组。