11554 sujets

JavaScript, DOM et API Web HTML5

Bonjour à tous
Je galère avec l'utilisation de ... dans des appels de fonctions
Soient deux fonctions:

function myFunction1(...items1) {
  // items1 est un array
}
function myFunction2(...items2) {  
  // items2 est un array
}


Si j'écris dans myFunction2

const result = myFunction1(items2);

myFunction1 reconnait un seul paramètre qui est un array, et non n paramètres.

Quelle est la bonne syntaxe pour appeler myFunction1 depuis myFunction2 en lui passant les contenu de items2 en paramètres ?
Modifié par Rodolphe (09 Aug 2025 - 16:59)
Modérateur
Hello,

Pourtant, c'est simple. Prenons cet exemple :

const sum = (...args) => args.reduce((a, b) => a + b)
let x = sum(1, 2, 3, 4)
console.log(x) // 10
let y = sum(1, 2, 3, 4, 5)
console.log(y) // 15


Si tu décortiques le code :

const sum = (...args) => {
  console.log(args)
  return args.reduce((a, b) => a + b)
}


Dans tous les cas, console.log te retourne un Array. Dans le premier cas,

[1,2,3,4]

Dans le deuxième cas :

[1,2,3,4,5]


Au passage, voilà la manière la plus élégante pour additionner des éléments d'un array. C'est une base. Il faut rajouter quelques exceptions.

Array.prototype.sum = function(){
  return this.reduce((a, b) => a + b)
}
let z = [123, 456, 789].sum()
console.log(z)


En laissant telquel :

let hello = ['hello', ' ', 'world'].sum()
console.log(hello)

Modifié par Niuxe (09 Aug 2025 - 22:09)
Merci de vos réponses
Le contexte

class MyClass2 {
// definit une classe d'objets
}
class MyClass3 {
// definit une classe d'objets
}
class MyClass1 {
  constructor(a, b, c, ...items) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.items = [];
    this.class2Items = [];
    this.class3Items = [];
    this.addItems(...items);
  }
  addItems(...items) {
    this.items = this.items.concat(items)
    for(const item of items) {
      if(typeof  == 'Object') {
        if(item instanceof MyClass2)
          this.class2Items.push(item);
        if(item instanceof MyClass3)
          this.class3Items.push(item);
      }
    }
  }
}
//ailleurs dans le code
const myObject = new MyClass1(x, y, z, o21, o31, t, q);
................
myObject.addItems(q, r, o32, s, o22);

Modifié par PapyJP (10 Aug 2025 - 17:57)
Modérateur
Hello,

Je vais être direct. C’est pour ton bien.

D’abord, tes noms de classes : MyClass1, MyClass2, MyClass3 ne veulent rien dire. Si tu veux de l’aide et que ton code soit lisible, choisis des noms qui reflètent leur rôle.

Exemple :

class Order{
    //...
}
class Customer{
    //...
}
class Product{
    //...
}


Ensuite, ce n’est pas parce que tu utilises class que tu fais de la POO. La POO n’est pas juste de la syntaxe, c’est un langage¹, un paradigme avec des règles, des concepts et des bonnes pratiques.

MyClass1 est un fourre-tout :
- Elle gère la liste globale items
- Elle gère les listes filtrées class2Items et class3Items
- Elle applique la logique de filtrage directement

Résultat, ce code est fragile.

La méthode addItems utilise instanceof pour distinguer les types. Si demain on ajoute MyClass4, tu devras modifier le code existant au lieu de simplement l’étendre. Utilise plutôt (c'est le chien de Mickey) le polyphormisme (pour faire simple) ou une interface commune avec une méthode comme getType().

MyClass1 dépend directement des classes concrètes MyClass2 et MyClass3. Cela rend le code rigide et difficile à tester. Je te recommande de dépendre des abstractions et non des class concrètes

Ton code est fragile, couplé, et peu évolutif. Si tu veux faire de la POO, travaille avec des abstractions, isole les responsabilités, et remplace-les instanceof par du polymorphisme. En l’état, chaque ajout ou changement va multiplier les modifications et les régressions.

En reprenant l'exemple des noms de class dans ma réponse. Voici un exemple (comme je te l'ai indiqué précédemment, un attribut² est soit privé soit protégé. Il n'est pas publique !). Par convention, un caractère underscore (souligné) devant.


// Produit de base
class Product {
    #name
    #price

    constructor(name, price) {
        this.#name = name;   
        this.#price = price;                   
    }

    getType() {
        return "generic";              
    }

    get name() {
        return this.#name;
    }

    get price() {
        return this.#price;
    }
}

// Produit spécialisé
class DigitalProduct extends Product {
    getType() {
        return "digital";
    }
}

class PhysicalProduct extends Product {
    getType() {
        return "physical";
    }
}

// Conteneur de produits
class Products {
    #items

    constructor() {
        this.#items = [];
    }

    add(...products) {
        this.#items.push(...products);
    }

    filterByType(type) {
        return this.#items.filter(p => p.getType() === type);
    }

    get items() {
        return [...this.#items]; // copie pour éviter modification directe
    }
}

// Commande
class Order {
    #customer
    #products

    constructor(customer, products) {
        this.#customer = customer;
        this.#products = products;
    }

    getTotal() {
        return this.#products.items.reduce((sum, p) => sum + p.price, 0);
    }

    get products() {
        return this.#products;
    }
}

// Client
class Customer {
    #name
    #orders

    constructor(name) {
        this.#name = name;
        this.#orders = [];
    }

    placeOrder(order) {
        this.#orders.push(order);
    }

    get name() {
        return this.#name;
    }

    get orders() {
        return [...this.#orders];
    }
}

// --- Démo ---
const p1 = new PhysicalProduct("Laptop", 1200);
const p2 = new DigitalProduct("E-book", 15);
const p3 = new PhysicalProduct("Mouse", 25);
const products = new Products()
products.add(p1, p2, p3)

const customer = new Customer("Alice");
const order = new Order(customer, products);

customer.placeOrder(order);

// Affichage
console.log(`Client : ${customer.name}`);
console.log(`Total commande : ${order.getTotal()} €`);
console.log(`Produits physiques :`, order.products.filterByType("physical"));
console.log(`Produits digitaux :`, order.products.filterByType("digital"));

_______
¹ l'UML
² La couleur de tes yeux est visible publiquement, mais cela fait partie de tes gènes qui sont privés ou protégés. Ce n'est pas un objet ou une personne autre que tes parents qui les ont colorés. Tes enfants ont peut-être la même couleur de tes yeux !
Modifié par Niuxe (10 Aug 2025 - 23:12)
Merci de cette réponse

Je suis tout à fait d'accord avec les concepts expliqués dans ton exemple.
J'ai fait partie des promoteurs en France de la POO à la fin des années 1980, je sais très bien ce dont il s'agit.

Le code que j'ai mis comme contexte n'est évidemment pas le code que j'utilise en réalité, c'était juste pour montrer l'utilisation de "..." dans mon environnement et pourquoi j'avais besoin de passer des ... en paramètres de mes fonctions.

En ce qui concerne la syntaxe de déclaration des propriétés, je suis toujours en attente d'indication d'un validateur online qui accepte cette syntaxe.
Ce qui a été dit dans cette discussion montre que -- sauf à installer un environnement de développement complexe -- il ne m'est pas possible de mettre ces recommandations en pratique telle quelles. J'en suis donc réduit à rester à une version passée de ES.
Modérateur
Tu ne lis pas le code que je t'ai mis en exemple Smiley ohwell . La méthode add ....


// Conteneur de produits
class Products {
    #items

    constructor() {
        this.#items = [];
    }

    add(...products) {
        this.#items.push(...products);
    }

    filterByType(type) {
        return this.#items.filter(p => p.getType() === type);
    }

    get items() {
        return [...this.#items]; // copie pour éviter modification directe
    }
}


Ensuite, si tu ne veux pas utiliser eslint, ton navigateur est un validateur javascript. Par contre, si tu veux que ton application fonctionne sur IE5, tu devras utiliser un transpiller/transcodeur comme babeljs

Le code que j'ai mis est valide pour les navigateurs récents. Avec ce code, IE8 renverra une pléthore d'erreurs.
Merci pour le push. Je vais utiliser ça plutôt que le concat que j’ai mis faute de mieux. Il faut que j’intègre l’utilisation des spread. Aurais tu un document bien fait sur les dernières versions de js ? Les seuls que j’ai trouvés sont des descriptions du langage, pas des documents “comment l’utiliser”.
Certes mon navigateur valide le code js mais ce n’est pas aussi facile d’emploi qu’un validateur.
Je ne sais pas ce que mes utilisateurs ont comme navigateur mais je ne crois pas que ça soit antérieur à IE9.
Modérateur
PapyJP a écrit :
Aurais tu un document bien fait sur les dernières versions de js ?


Il y a toujours
- l'ecma.
- tout Javascript
- la mdn

En livre, JavaScript: The Definitive Guide, 7th Edition (un excellent bouquin)

PapyJP a écrit :

Les seuls que j’ai trouvés sont des descriptions du langage, pas des documents “comment l’utiliser”.


???? Smiley kneu

le JS ou le spread operator ?

PapyJP a écrit :

Certes mon navigateur valide le code js mais ce n’est pas aussi facile d’emploi qu’un validateur.


Il te remonte les erreurs et même les warnings. Il suffit de le configurer correctement. Quand je développe, la console est constamment ouverte. Même le bundler peut remonter des erreurs. Tu n'as même plus besoin de configurer babel avec certains bundler

Bundler : Gulp, Grunt, Webpack, etc.

PapyJP a écrit :

Je ne sais pas ce que mes utilisateurs ont comme navigateur mais je ne crois pas que ça soit antérieur à IE9.


- Matomo (anciennement Piwik et PHPMyVisite)
- gg Analytics
Modifié par Niuxe (12 Aug 2025 - 14:53)
Merci de ta réponse

Ce que je veux dire c'est que la plupart des documents te donnent une liste d'objets ou de fonctions en ordre alphabétique, avec des exemples tellement basiques qu'ils ne servent pas à grand chose.
Par exemple, si je recherche push + spread les documents référencés ne donnent aucun exemple de array.push(...items)
A postériori une telle expression semble triviale, mais encore faut il en avoir l'idée.

Je vais acquérir le bouquin que tu recommandes et voir si cela peut m'être utile.
Modérateur
Il vaut mieux des exemples basiques afin de mieux comprendre un concept. Parce qu'après dans le développement, c'est nettement plus compliqué.

Le spread operator (...) en JavaScript sert surtout à décomposer un iterable (comme un tableau, une chaîne, un objet itérable) en ses éléments individuels, pour les insérer ailleurs.

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]



let arr = [...Array(6).keys()]
let array_in_array = []
let only_array = []
array_in_array.push(arr)
only_array.push(...arr)
console.log(array_in_array) // [[0, 1, 2, 3, 4, 5]] un array dans un array
console.log(only_array) // [0, 1, 2, 3, 4, 5] un array


Pour comprendre ce code ci-dessous, il faut consulter la signature de la méthode .push(). Tu constateras que tu peux ajouter autant d'elements que nécessaire (elementN)


add(...products) {
    this.#items.push(...products);
}


Dans cette méthode add(...products), le paramètre ...products utilise le rest parameter qui permet de recueillir un nombre indéfini d’arguments passés à la fonction sous forme d’un tableau appelé products.

Ensuite, dans le corps de la méthode, this.#items.push(...products) utilise le spread operator pour décomposer ce tableau products en éléments individuels, afin de les passer un par un à la méthode push. Cela revient donc à écrire push(product1, product2, ..., productN).

Ainsi, cette méthode add permet d’ajouter un nombre variable d’éléments dans this.#items de manière pratique et concise.
Modifié par Niuxe (13 Aug 2025 - 03:20)