[JS] Je n'ai pas bien compris Array.map()

Le problème exposé dans ce sujet a été résolu.

Salut !

Je ne comprends pas pourquoi le code suivant ne renvoie pas un tableau tab_test contenant 10 fois l’élément 'power' :

var empty = new Array(10);

var tab_test = empty.map(e => 'power');

Le tableau tab_test reste vide…

+0 -0

Je ne comprends pas pourquoi le code suivant ne renvoie pas un tableau tab_test contenant 10 fois l’élément 'power' :

var empty = new Array(10);

var tab_test = empty.map(e => 'power');

Le tableau tab_test reste vide…

LysPrintemps

Salut,

Après quelques tests j’ai l’impression qu'Array(10) crée un tableau avec une capacité de 10 mais ne comprenant pas d’éléments. La fonction du map n’est alors appliquée sur aucun élément.

En soi, le problème ne vient pas de la fonction map mais de la manière dont tu utilises les tableaux.

new Array(10) crée un tableau capable d’obtenir 10 éléments (length=10) mais ne lui met aucun élément. Du coup quand map passe, il pense que c’est un tableau vide, et ne le gère pas.

ici il faudrait avoir un tableau avec des valeurs tel que : new Array(10).fill(0).

Hello,

Voici un peu de théorie pour expliquer la philosophie de la chose.

Les tableaux sont des objets JavaScript comme les autres : ils ont un prototype et des propriétés propres. Le prototype est l’objet dont ils héritent des propriétés : c’est l’objet Array.prototype dans le cas des tableaux, dont ils héritent par exemple map et fill. length est l’unique propriété propre du tableau empty que tu as créé. Tu peux lui en ajouter d’autres par la suite.

var empty = new Array(10);
// ci-dessous on vérifie le prototype du tableau empty
Object.getPrototypeOf(empty) === Array.prototype; // --> true (dans une console js)
// ci-dessous on énumère les propriétés propres du tableau empty
Object.getOwnPropertyNames(empty); // --> ["length"]
// ci-dessous on énumère les propriétés que empty et les autres tableaux héritent de l'objet Array.prototype
Object.getOwnPropertyNames(Array.prototype); // --> ["length", "constructor", "concat", "copyWithin", "fill", "find", "findIndex", "pop", "push", "reverse", "shift", "unshift", "slice", "sort", "splice", "includes", "indexOf", "keys", "entries", "forEach", "filter", "map", "every", "some", "reduce", "reduceRight", "toString", "toLocaleString", "join", "lastIndexOf", "values", "flat", "flatMap"]

Les cases d’un tableau sont elles-mêmes des propriétés à peu près comme les autres, dont le nom se trouve être un numéro compris entre 0 et la valeur de la propriété length.

var toto = ["a", "b", "c"];
Object.getOwnPropertyNames(toto); // --> ["0", "1", "2", "length"]
toto.length; // --> 3
// on peut ajouter une case a posteriori, length est automagiquement mis à jour
toto[3] = "d";
toto.length; // --> 4
Object.getOwnPropertyNames(toto); // --> ["0", "1", "2", "3", "length"]
// si on réduit length, on perd des cases
toto.length = 2;
Object.getOwnPropertyNames(toto); // --> ["0", "1", "length"]

Comme toutes les autres propriétés, les cases d’un tableau peuvent être absentes : on dit dans ce cas que le tableau a des trous.

// un tableau avec des trous
Object.getOwnPropertyNames(["a",,,,"b",,,,"c"]); // --> ["0", "4", "8", "length"]
// un tableau avec 10 trous
var empty = new Array(10);
Object.getOwnPropertyNames(empty); // --> ["length"]
// on remplit toutes les cases (trou ou pas)
empty.fill("toto");
Object.getOwnPropertyNames(empty); // --> ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "length"]
// on injecte un trou
delete empty[5];
Object.getOwnPropertyNames(empty); // --> ["0", "1", "2", "3", "4", "6", "7", "8", "9", "length"]

La fonction Array.prototype.map ne s’occupe pas des trous dans un tableau : c’est seulement pour les cases où il y a bien un élément qu’elle appliquera la fonction donnée en paramètre. J’en profite pour soulever une petite subtilité : si une case contienne undefined, la case est bien présente, ce n’est pas un trou, bien que lorsque qu’on essaye d’accéder à une case qui est un trou on obtienne par convention l’objet undefined. D’où les résultats ci-dessous, où empty est la convention d’affichage de Chrome pour les trous :

["a",,,,"b",,,,"c"].map(e => "power"); // --> ["power", empty × 3, "power", empty × 3, "power"]
new Array(10).map(e => "power"); // --> [empty × 10]
[0, 1, 2].map(e => "power"); // --> ["power", "power", "power"]
[undefined, , undefined].map(e => "power"); // --> ["power", empty, "power"]
+3 -0

Merci bien pour vos réponses, ça fait plaisir !

Avant que je n’oublie, j’aimerais poser encore une petite question théorique : quelle est la différence entre les lignes 2 et 3 du code ci-dessous ?

var e, f;
e = Array(10);
f = new Array(10);

// Étonnant : affiche 'false'
console.log(e == f);

@dentuk : Merci particulièrement à toi, ça me fait plaisir d’avoir une réponse aussi complète !
Je prendrai un peu de temps pour la lire et la comprendre, je reviens sur le sujet si j’ai des questions.

Il n’y a pas de différence fondamentale entre Array(10) et new Array(10), pour moi la première syntaxe est gardée pour des raisons de rétro-compatibilité, tandis que la deuxième correspond mieux à la logique du JavaScript puisqu’on veut créer un nouvel objet.

Meme si les deux tableaux étaient créés avec la même syntaxe, tu obtiendrais false car les tableaux sont des objets et le test d’égalité entre deux objets consiste juste à vérifier si les deux objets sont les mêmes (au sens «référence mémoire»), ce n’est pas un test plus poussé qui irait vérifier les valeurs des propriétés.

+0 -0

Il n’y a pas de différence fondamentale entre Array(10) et new Array(10),

en soi. si.

new Array(10) crée un objet de type array, en donnant pour argument 10 à son constructeur. Array(10) appelle la fonction Array (qui s’avère faire la même chose que le constructeur) mais cette fonction ne retourne rien (donc undefined).

function bla(b) {this.b = b;}
bla(10)
// undefined
new bla(10)
// Object { b: 10 }

mais cette fonction ne retourne rien

bla(10) ne retourne rien, mais Array(10) retourne bien un nouveau tableau comme le ferait new Array(10). Je suis d’accord qu’appeler une fonction ou l’utiliser comme constructeur ne donne pas le même résultat en général, mais pour la fonction Array si. C’est pour cette raison que je pense que la syntaxe sans le new est uniquement conservée par rétro-compatibilité, bien qu’elle soit trompeuse pour les personnes qui essaient de comprendre la différence entre un appel de fonction et l’utilisation d’une fonction comme constructeur. Je ne sais pas comment il fallait interpréter la question de LysPrintemps entre cas général et cas particulier mais comme ça il aura les deux réponses !

+0 -0

En effet pour Array, ça le fait comme ça, encore une incohérence due à l’histoire de JS…

Quoi que, j’ai souvenir qu’avec Number ça marche aussi. Mais bon faut faire gaffe c’est un bon moyen de se gourer, avec ce genre de cas par exemple :

typeof(Number(1))
// "number"
typeof(new Number(1))
// "object"

Salut,

J’ai essayé de comprendre la différence entre Array(10) et new Array(10) en créant mon propre objet Tableau :

function Tableau(taille)
{
    this.taille = taille;
    return this;
}


var petit  =  Tableau(5);
var grand  =  new Tableau(150);

console.log(petit);  // Renvoie Window
console.log(grand);  // Renvoie Object { taille: 150 }

Et là, c’est encore plus confus car la ligne 8 ne revoie même pas un objet de type Tableau… Qu’est-ce qui m’échappe dans cet exemple ?

PS : en utilisant 'use strict'; au début du code, ce dernier plante avec l’erreur que this n’est pas défini à la ligne 3…

Tu fait du JavaScript qui date de l’age de pierre :P

le faite de définir une classe avec ta fonction Tableau entraine la contraite suivante:

Vue que tu exécute ce code dans un navigateur, tu as un contexte, du coup ta référence this pointe vers window, l’objet qui gère ta fenêtre de navigateur, c’est donc normal qu’il te retorune window et non un objet tableau. D’où l’importance du mot clef new ici.

Peut-être que d’autre pourrons mieux t’expliqué que moi ceci.

Je me suis mal exprimé sur la différence entre Array(10) et new Array(10) : les deux retournent bien la même chose, mais c’est spécifique à la fonction Array. Elle va vérifier la manière dont elle a été appelée (appel ou construction) pour s’assurer de retourner la même chose dans les deux cas (un nouveau tableau). Voici comment tu peux écrire un code similaire côté JS, en espérant que ça soit plus clair que des mots :

function Tableau(taille)
{
    // si on détecte qu'on est sur un appel [Tableau(10)] et non sur une construction [new Tableau(10)]
    if (new.target === undefined) {
        // on retourne le même résultat que celui qu'on aurait obtenu via une construction
        return new Tableau(taille);
    }
    // sinon on est sur une construction, on complète le this comme on le fait habituellement dans un constructeur
    this.taille = taille;
}

Quand toi tu écris une fonction, ça ne sert à rien de vérifier comment elle a été appelée, normalement tu sais si tu as voulu écrire une fonction ou un constructeur et c’est toi qui choisiras en conséquence d’appeler avec ou sans le new.

+0 -0

Merci pour l’explication :)

J’ai réussi à trouver une autre manière de procéder :

'use strict';

function Tableau(taille)
{
    if (this === undefined)
    {
        console.log("this n'est pas défini.");
        return new Tableau(taille);
    }

    console.log("this est défini :");
    console.log(this);

    this.taille = taille;
}

Tableau(10);

Je trouve cependant qu’elle n’est pas très propre.
En théorie, on n’est pas certain que this existe.
Or, on compare sa valeur à la ligne 5.

Pour moins que ça, je pense qu’un programme en C aurait planté si on avait écrit un code similaire.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte