【问题标题】:Why do I need to copy an array to use a method on it?为什么我需要复制一个数组才能在其上使用方法?
【发布时间】:2018-11-27 02:35:47
【问题描述】:

我可以使用Array() 来创建一个包含固定数量的未定义条目的数组。例如

Array(2); // [empty × 2] 

但是如果我去使用 map 方法,比如说,在我的新数组上,条目仍然是未定义的:

Array(2).map( () => "foo");  // [empty × 2] 

如果我复制数组,那么 map 确实有效:

[...Array(2)].map( () => "foo");  // ["foo", "foo"]

为什么我需要一个副本才能使用数组?

【问题讨论】:

  • [...Array(2)]的结果是什么
  • @Kain0_0 它是[未定义,未定义]。并且 Array.isArray(Array(2)) 是真的。
  • 也可以使用fill()Array(2).fill("foo"); // ["foo", "foo"] 只需小心传递要填充的对象,因为所有元素都是相同的引用
  • JavaScript 中没有像 index out of bound error 这样的东西。

标签: javascript arrays


【解决方案1】:

当你使用Array(arrayLength)创建数组时,你会have

一个新的 JavaScript 数组,其长度属性设置为该数字(注意:这意味着一个 arrayLength 空槽数组,而不是具有实际未定义值的槽)。

该数组实际上不包含任何值,甚至不包含 undefined 值 - 它只是有一个 length 属性。

当您将具有length 属性的可迭代对象展开到数组中时,展开语法会访问每个索引并在新数组中的该索引处设置值。例如:

const arr1 = [];
arr1.length = 4;
// arr1 does not actually have any index properties:
console.log('1' in arr1);

const arr2 = [...arr1];
console.log(arr2);
console.log('2' in arr2);

.map 仅映射属性实际存在于您要映射的数组中的属性/值。

使用数组构造函数是令人困惑的。我建议使用 Array.from 代替,从头开始创建数组时 - 您可以将具有 length 属性的对象以及映射函数传递给它:

const arr = Array.from(
  { length: 2 },
  () => 'foo'
);
console.log(arr);

【讨论】:

  • 我在一门课程中看到Array(),它让我想起了Python中的一些有用的东西,但遗憾的是它可能没那么有用。干杯。
  • 曾经是一个不错的选择,在Array.from可用之前,但现在我认为没有任何理由使用它,它有太多的潜力混乱。
  • 嘿,这里需要你的帮助stackoverflow.com/questions/68834433/…
【解决方案2】:

原因是数组元素未赋值。 See here第一段描述。 ...回调仅针对具有赋值的数组索引调用,包括未定义的。

考虑:

var array1 = Array(2);
array1[0] = undefined;

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(array1);
console.log(map1);

输出:

Array [undefined, undefined]
Array [NaN, undefined]

当打印数组时,它的每个元素都会被询问。第一个已分配undefined,另一个默认为undefined

映射操作调用第一个元素的映射操作,因为它已被定义(通过赋值)。它不调用第二个参数的映射操作,只是传递了undefined

【讨论】:

    【解决方案3】:

    正如@CertainPerformance 所指出的,您的数组除了length 之外没有任何属性,您可以使用以下行验证这一点:new Array(1).hasOwnProperty(0),它返回false

    查看15.4.4.19 Array.prototype.map可以看到,在7.3,有一个检查key是否存在于数组中。

    1..6。 [...]
    7. 重复, 而 k len

    1. Pk 为 ToString(k)。

    2. kPresent 是调用 [[HasProperty]] 的结果 O 的内部方法,带有参数 Pk

    3. 如果 kPresenttrue,则
      1. kValue 是调用 [[Get]] 内部的结果 O 的方法,带有参数 Pk

      2. mappedValue 是调用 [[Call]] 内部的结果 以 T 作为 thiscallbackfn 方法 包含 kValuekO.

      3. 称呼 A 的 [[DefineOwnProperty]] 内部方法 参数Pk,属性描述符{[[Value]]:mappedValue, [[Writable]]: true, [[Enumerable]]: true, [[可配置]]:true}和false

    4. 增加 k 减 1。

    9. 返回A

    【讨论】:

      【解决方案4】:

      正如已经指出的,Array(2) 只会创建一个包含两个无法映射的空槽的数组。

      someevery而言,Array(2)确实是一个空数组:

      Array(2).some(() => 1 > 0); //=> false
      Array(2).every(() => 1 < 0); //=> true
      

      但是,这个空槽数组可以以某种方式“迭代”:

      [].join(','); //=> ''
      Array(2).join(','); //=> ','
      
      JSON.stringify([]) //=> '[]'
      JSON.stringify(Array(2)) //=> '[null,null]'
      

      所以我们可以利用这些知识提出有趣且有点讽刺的方法来在此类“空”数组上使用 ES5 数组函数。

      你自己想出了那个:

      [...Array(2)].map(foo);
      

      @charlietfl 建议的变体:

      Array(2).fill().map(foo);
      

      我个人推荐这个:

      Array.from(Array(2)).map(foo);
      

      有点老派:

      Array.apply(null, Array(2)).map(foo);
      

      这个比较啰嗦:

      Array(2).join(',').split(',').map(foo);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-31
        • 1970-01-01
        • 2019-12-23
        • 1970-01-01
        • 1970-01-01
        • 2013-10-02
        • 2015-08-08
        相关资源
        最近更新 更多