web-dev-qa-db-fra.com

Pourquoi dois-je copier un tableau pour utiliser une méthode dessus?

Je peux utiliser Array() pour avoir un tableau avec un nombre fixe d'entrées non définies. Par exemple

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

Mais si je vais utiliser la méthode map, disons, sur mon nouveau tableau, les entrées ne sont toujours pas définies:

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

Si je copie le tableau, alors la carte fonctionne:

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

Pourquoi ai-je besoin d'une copie pour utiliser le tableau?

25
cham

Lorsque vous utilisez Array(arrayLength) pour créer un tableau, vous aurez aurez :

un nouveau tableau JavaScript dont la propriété length est définie sur ce nombre (Remarque: cela implique un tableau d'emplacements vides arrayLength, et non d'emplacements avec des valeurs indéfinies réelles).

En réalité, le tableau ne contient aucune valeur, pas même les valeurs undefined. Il possède simplement une propriété length.

Lorsque vous étendez un objet itérable avec une propriété length dans un tableau, la syntaxe de propagation accède à chaque index et définit la valeur correspondant à cet index dans le nouveau tableau. Par exemple:

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);

Et .map mappe uniquement les propriétés/valeurs pour lesquelles la propriété existe réellement dans le tableau sur lequel vous mappez.

L'utilisation du constructeur de tableau est source de confusion. Je suggère d'utiliser plutôt Array.from, lors de la création de tableaux à partir de rien - vous pouvez lui transmettre un objet avec une propriété length, ainsi qu'une fonction de mappage:

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

48
CertainPerformance

La raison en est que l'élément de tableau n'est pas attribué. Voir ici le premier paragraphe de la description. Le rappel ... n'est appelé que pour les index du tableau auxquels des valeurs ont été attribuées, y compris indéfini.

Considérer:

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);

Les sorties:

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

Lorsque le tableau est imprimé, chacun de ses éléments est interrogé. Le premier a été attribué à undefined, l'autre à undefined par défaut.

L'opération de mappage appelle l'opération de mappage pour le premier élément car elle a été définie (via une affectation). Il n'appelle pas l'opération de mappage pour le deuxième argument et transmet simplement undefined.

9
Kain0_0

Comme l'a souligné @CertainPerformance, votre tableau n'a aucune propriété en plus de sa variable length. Vous pouvez vérifier cela avec cette ligne: new Array(1).hasOwnProperty(0), qui renvoie false.

En regardant 15.4.4.19 Array.prototype.map vous pouvez voir qu’en 7.3, on vérifie s’il existe une clé dans le tableau.

1..6. [...]
7. Répéter, tandis que len

  1. Laisser Pk être ToString (k).

  2. Laisser kPresent être le résultat d'appeler le [[HasProperty]] méthode interne de O avec argument Pk.

  3. Si kPresent est vrai, puis</ p>
    1. Laisser kValue être le résultat d'appeler le [[Get]] internal méthode de O avec argument Pk.

    2. Laisser mappedValue être le résultat d'appeler le [[Call]] internal méthode de callbackfn avec T comme le ce liste de valeurs et d'arguments contenant kValuek, et O.

    3. Appel la méthode interne [[DefineOwnProperty]] de UNE avec arguments Pk, Descripteur de propriété {[[Valeur]]: mappedValue, [[Writable]]: vrai, [[Enumerable]]: vrai, [[Configurable]]: vrai}, et false.

  4. Augmenter k par 1.

UNE
2
Moritz Roessler

Comme indiqué précédemment, Array(2) ne créera qu'un tableau de deux emplacements vides sur lesquels il est impossible de mapper.

En ce qui concerne some et every sont concernés, Array(2) est en effet un tableau vide:

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

Cependant, ce tableau d'emplacements vides peut en quelque sorte être "itéré" sur:

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

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

Nous pouvons donc utiliser ces connaissances pour trouver des moyens intéressants et quelque peu ironiques d’utiliser les fonctions de tableau ES5 sur de tels tableaux "vides".

Vous en avez une vous-même:

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

Une variante d'une suggestion de @charlietfl:

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

Personnellement, je recommanderais celui-ci:

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

Un peu vieille école:

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

Celui-ci est assez prolixe:

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