J'ai observé cela dans Firefox-3.5.7/Firebug-1.5.3 et Firefox-3.6.16/Firebug-1.6.2
Quand j'allume Firebug:
>>> x = new Array(3)
[undefined, undefined, undefined]
>>> y = [undefined, undefined, undefined]
[undefined, undefined, undefined]
>>> x.constructor == y.constructor
true
>>> x.map(function(){ return 0; })
[undefined, undefined, undefined]
>>> y.map(function(){ return 0; })
[0, 0, 0]
Que se passe t-il ici? Est-ce un bug ou est-ce que je ne comprends pas comment utiliser new Array(3)
?
Il semble que le premier exemple
x = new Array(3);
Crée un tableau avec des pointeurs indéfinis.
Et le second crée un tableau avec des pointeurs sur 3 objets non définis. Dans ce cas, les pointeurs eux-mêmes ne sont PAS indéfinis, mais uniquement les objets sur lesquels ils pointent.
y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];
Comme la carte est exécutée dans le contexte des objets du tableau, je pense que la première carte ne parvient pas à exécuter la fonction, tandis que la seconde parvient à s'exécuter.
J'avais une tâche dont je ne connaissais que la longueur et que je devais transformer les éléments. Je voulais faire quelque chose comme ça:
let arr = new Array(10).map((val,idx) => idx);
Pour créer rapidement un tableau comme celui-ci:
[0,1,2,3,4,5,6,7,8,9]
Mais cela n'a pas fonctionné parce que: voir la réponse de Jonathan Lonowski plus haut.
La solution pourrait être de remplir les éléments du tableau avec n'importe quelle valeur (même avec undefined) en utilisant Array.prototype.fill ()
let arr = new Array(10).fill(undefined).map((val,idx) => idx);
console.log(new Array(10).fill(undefined).map((val, idx) => idx));
Mettre à jour
Une autre solution pourrait être:
let arr = Array.apply(null, Array(10)).map((val, idx) => idx);
console.log(Array.apply(null, Array(10)).map((val, idx) => idx));
Avec ES6, vous pouvez faire [...Array(10)].map((a, b) => a)
, rapidement et facilement!
Solution ES6:
[...Array(10)]
Ne fonctionne pas sur TypeScript (2.3), cependant
Les tableaux sont différents. La différence est que new Array(3)
crée un tableau d'une longueur de trois mais pas de propriétés, tandis que [undefined, undefined, undefined]
crée un tableau d'une longueur de trois et trois propriétés appelées "0", "1" et "2", chacune avec une valeur de undefined
. Vous pouvez voir la différence en utilisant l'opérateur in
:
"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true
Cela provient du fait légèrement déroutant que si vous essayez d'obtenir la valeur d'une propriété inexistante d'un objet natif en JavaScript, il retourne undefined
(plutôt que de générer une erreur, comme cela se produit lorsque vous essayez de vous référer variable inexistante), ce qui est identique à ce que vous obtenez si la propriété a déjà été explicitement définie sur undefined
.
Depuis la page MDC pour map
:
[...]
callback
n'est appelé que pour les index du tableau auxquels une valeur a été attribuée; [...]
[undefined]
Applique le paramètre sur l'index (s) de sorte que map
itérera, alors que new Array(1)
initialise simplement l'index (s) avec une valeur par défaut de undefined
so map
le saute.
Je crois que c'est la même pour tous les méthodes d'itération .
Je pense que la meilleure façon d’expliquer cela est d’examiner la façon dont Chrome le gère.
>>> x = new Array(3)
[]
>>> x.length
3
Donc, ce qui se passe réellement, c'est que new Array () renvoie un tableau vide qui a une longueur de 3, mais aucune valeur. Par conséquent, lorsque vous exécutez x.map
sur un tableau techniquement vide, il n'y a rien à définir.
Firefox "remplit" ces emplacements vides avec undefined
même s'il n'a aucune valeur.
Je ne pense pas que ce soit explicitement un bug, mais simplement une mauvaise façon de représenter ce qui se passe. Je suppose que Chrome est "plus correct" car il montre qu'il n'y a rien en réalité dans le tableau.
Dans la spécification ECMAScript 6ème édition.
new Array(3)
définit uniquement la propriété length
et ne définit pas de propriétés d'index telles que {length: 3}
. voir https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len Étape 9.
[undefined, undefined, undefined]
Définira les propriétés de l'index et de la longueur, comme {0: undefined, 1: undefined, 2: undefined, length: 3}
. voir https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulationElementList
étape 5.
méthodes map
, every
, some
, forEach
, slice
, reduce
, reduceRight
, filter
of Array vérifiera la propriété index par HasProperty
méthode interne, de sorte que new Array(3).map(v => 1)
n'appellera pas le rappel.
pour plus de détails, voir https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map
Comment réparer?
let a = new Array(3);
a.join('.').split('.').map(v => 1);
let a = new Array(3);
a.fill(1);
let a = new Array(3);
a.fill(undefined).map(v => 1);
let a = new Array(3);
[...a].map(v => 1);
Juste couru dans cela. Il serait pratique d’utiliser Array(n).map
.
Array(3)
donne approximativement {length: 3}
[undefined, undefined, undefined]
crée les propriétés numérotées:{0: undefined, 1: undefined, 2: undefined, length: 3}
.
L'implémentation map () n'agit que sur les propriétés définies.
Pas un bug. C'est ainsi que le constructeur Array est défini pour fonctionner.
De MDC:
Lorsque vous spécifiez un paramètre numérique unique avec le constructeur Array, vous indiquez la longueur initiale du tableau. Le code suivant crée un tableau de cinq éléments:
var billingMethod = new Array(5);
Le comportement du constructeur Array dépend de si le paramètre unique est un nombre.
La méthode .map()
inclut uniquement dans l'itération des éléments du tableau auxquels des valeurs ont été explicitement attribuées. Même une affectation explicite de undefined
fera en sorte qu'une valeur soit considérée comme éligible pour l'inclusion dans l'itération. Cela semble étrange, mais c'est essentiellement la différence entre une propriété explicite undefined
sur un objet et une propriété manquante:
var x = { }, y = { z: undefined };
if (x.z === y.z) // true
L'objet x
ne possède pas de propriété appelée "z", et l'objet y
. Cependant, dans les deux cas, il apparaît que la "valeur" de la propriété est undefined
. Dans un tableau, la situation est similaire: la valeur de length
effectue implicitement une attribution de valeur à tous les éléments de zéro à length - 1
. La fonction .map()
ne fera donc rien (n'appelle pas le rappel) lorsqu'elle est appelée sur un tableau nouvellement construit avec le constructeur Array et un argument numérique.
Si vous faites cela pour remplir facilement un tableau avec des valeurs, vous ne pouvez pas utiliser remplissage pour des raisons de support du navigateur et vous ne voulez vraiment pas faire de boucle for, vous pouvez aussi faire x = new Array(3).join(".").split(".").map(...
qui vous donnera un tableau de chaînes vides.
Assez moche, je dois le dire, mais au moins le problème et l'intention sont clairement communiqués.
Voici une méthode d’utilité simple comme solution de contournement:
Simple mapFor
function mapFor(toExclusive, callback) {
callback = callback || function(){};
var arr = [];
for (var i = 0; i < toExclusive; i++) {
arr.Push(callback(i));
}
return arr;
};
var arr = mapFor(3, function(i){ return i; });
console.log(arr); // [0, 1, 2]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]
Exemple complet
Voici un exemple plus complet (avec vérifications de sécurité) qui permet également de spécifier un index de départ facultatif:
function mapFor() {
var from, toExclusive, callback;
if (arguments.length == 3) {
from = arguments[0];
toExclusive = arguments[1];
callback = arguments[2];
} else if (arguments.length == 2) {
if (typeof arguments[1] === 'function') {
from = 0;
toExclusive = arguments[0];
callback = arguments[1];
} else {
from = arguments[0];
toExclusive = arguments[1];
}
} else if (arguments.length == 1) {
from = 0;
toExclusive = arguments[0];
}
callback = callback || function () {};
var arr = [];
for (; from < toExclusive; from++) {
arr.Push(callback(from));
}
return arr;
}
var arr = mapFor(1, 3, function (i) { return i; });
console.log(arr); // [1, 2]
arr = mapFor(1, 3);
console.log(arr); // [undefined, undefined]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]
décompter
La manipulation de l'index transmis au rappel permet de compter à rebours:
var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
return count - 1 - i;
});
// arr = [2, 1, 0]