Getting started General
Components
Forms
Trends
Utilities
Migrate from v1
  Join us
  JavaScript Articles

Comment utiliser forEach pour un objet en JavaScript : guide complet

JavaScript

Apprenez a iterer sur des objets JavaScript en utilisant forEach avec Object.keys(), Object.values() et Object.entries(). Guide complet avec exemples, conseils de performance et cas d'usage concrets.

Comment utiliser forEach pour un objet en JavaScript : guide complet
Comment utiliser forEach pour un objet en JavaScript : guide complet

La methode forEach() en JavaScript est une methode puissante pour les tableaux qui permet d'iterer sur les elements d'un tableau, mais elle ne peut pas etre utilisee directement sur des objets. En revanche, vous pouvez iterer sur des objets JavaScript en utilisant forEach() en convertissant d'abord l'objet en tableau avec Object.keys(), Object.values() ou Object.entries().

Ce guide complet explore toutes les methodes pour iterer sur des objets JavaScript en utilisant forEach(), des implementations de base aux techniques avancees. Vous apprendrez quand utiliser chaque methode, les considerations de performance, et des exemples concrets qui vous aideront a ecrire du code JavaScript plus efficace et plus maintenable.

Pourquoi ne peut-on pas utiliser forEach() directement sur des objets ?

Les objets JavaScript ne sont pas iterables par defaut, ce qui signifie qu'ils n'ont pas de methodes d'iteration integrees comme forEach(), map() ou filter(). Ces methodes ne sont disponibles que sur les tableaux et d'autres objets iterables.

const person = {
  name: 'John',
  age: 30,
  city: 'New York'
};

// This will throw an error
// person.forEach(); // TypeError: person.forEach is not a function

// Objects don't have forEach method
console.log(typeof person.forEach); // undefined

Pour utiliser forEach() avec des objets, vous devez d'abord convertir l'objet en tableau. JavaScript fournit trois methodes principales pour cela : Object.keys(), Object.values() et Object.entries().

Methode 1 : utiliser Object.keys() avec forEach()

La methode Object.keys() renvoie un tableau contenant les noms des proprietes enumerables propres (own) d'un objet. Vous pouvez ensuite utiliser forEach() pour iterer sur ces cles et acceder aux valeurs correspondantes.

Utilisation de base

const person = {
  name: 'John',
  age: 30,
  city: 'New York',
  occupation: 'Developer'
};

// Iterate over object keys using forEach
Object.keys(person).forEach(key => {
  console.log(`${key}: ${person[key]}`);
});

// Output:
// name: John
// age: 30
// city: New York
// occupation: Developer

Exemple avance avec index

const user = {
  firstName: 'Alice',
  lastName: 'Smith',
  email: '[email protected]',
  age: 28,
  isActive: true
};

// forEach provides index as second parameter
Object.keys(user).forEach((key, index) => {
  console.log(`Property ${index + 1}: ${key} = ${user[key]}`);
});

// Output:
// Property 1: firstName = Alice
// Property 2: lastName = Smith
// Property 3: email = [email protected]
// Property 4: age = 28
// Property 5: isActive = true

Filtrer et traiter des cles

const config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3,
  debug: false,
  version: '1.0.0'
};

// Filter and process only string values
Object.keys(config).forEach(key => {
  const value = config[key];
  if (typeof value === 'string') {
    console.log(`String config: ${key} = "${value}"`);
  }
});

// Output:
// String config: apiUrl = "https://api.example.com"
// String config: version = "1.0.0"

Avantages :

  • Syntaxe simple et directe
  • Bonnes performances pour la plupart des cas
  • Donne acces aux cles et aux valeurs
  • Fonctionne avec toutes les proprietes enumerables

Inconvenients :

  • Necessite d'acceder aux valeurs via la notation entre crochets
  • N'itere que sur les proprietes enumerables propres (own)
  • N'inclut pas les proprietes heritees

Methode 2 : utiliser Object.values() avec forEach()

La methode Object.values() renvoie un tableau des valeurs des proprietes enumerables propres (own) d'un objet. C'est utile quand vous n'avez besoin que des valeurs et pas des cles.

Utilisation de base

const scores = {
  math: 95,
  science: 87,
  english: 92,
  history: 78
};

// Iterate over object values using forEach
Object.values(scores).forEach(score => {
  console.log(`Score: ${score}`);
});

// Output:
// Score: 95
// Score: 87
// Score: 92
// Score: 78

Calculer des statistiques

const sales = {
  january: 15000,
  february: 18000,
  march: 22000,
  april: 19000,
  may: 25000
};

let total = 0;
let count = 0;

// Calculate total and count using forEach
Object.values(sales).forEach(amount => {
  total += amount;
  count++;
});

const average = total / count;
console.log(`Total sales: $${total.toLocaleString()}`);
console.log(`Average sales: $${average.toLocaleString()}`);

// Output:
// Total sales: $99,000
// Average sales: $19,800

Traiter des valeurs complexes

const products = {
  laptop: { price: 999, category: 'electronics', inStock: true },
  book: { price: 19.99, category: 'education', inStock: false },
  phone: { price: 699, category: 'electronics', inStock: true },
  pen: { price: 2.99, category: 'office', inStock: true }
};

// Process product values
Object.values(products).forEach(product => {
  if (product.inStock) {
    console.log(`${product.category}: $${product.price} (In Stock)`);
  }
});

// Output:
// electronics: $999 (In Stock)
// electronics: $699 (In Stock)
// office: $2.99 (In Stock)

Avantages :

  • Acces direct aux valeurs sans lookup de cle
  • Syntaxe propre quand vous n'avez besoin que des valeurs
  • Pratique pour des calculs et des aggregations
  • Efficace pour les operations basees uniquement sur les valeurs

Inconvenients :

  • Pas d'acces aux cles pendant l'iteration
  • Impossible de modifier facilement l'objet d'origine
  • Moins flexible que Object.entries()

Methode 3 : utiliser Object.entries() avec forEach()

La methode Object.entries() renvoie un tableau de paires [key, value] (cles en string) des proprietes enumerables propres (own) d'un objet. C'est la methode la plus flexible car elle donne acces aux cles et aux valeurs en meme temps.

Utilisation de base

const person = {
  name: 'John',
  age: 30,
  city: 'New York'
};

// Iterate over object entries using forEach
Object.entries(person).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});

// Output:
// name: John
// age: 30
// city: New York

Destructuring avec index

const settings = {
  theme: 'dark',
  language: 'en',
  notifications: true,
  autoSave: false
};

// Using destructuring with index
Object.entries(settings).forEach(([key, value], index) => {
  console.log(`Setting ${index + 1}: ${key} = ${value}`);
});

// Output:
// Setting 1: theme = dark
// Setting 2: language = en
// Setting 3: notifications = true
// Setting 4: autoSave = false

Traitement conditionnel

const user = {
  name: 'Alice',
  email: '[email protected]',
  age: 28,
  isAdmin: false,
  lastLogin: '2025-01-27'
};

// Process entries conditionally
Object.entries(user).forEach(([key, value]) => {
  if (typeof value === 'string' && value.includes('@')) {
    console.log(`Email field found: ${key} = ${value}`);
  } else if (typeof value === 'boolean') {
    console.log(`Boolean field: ${key} = ${value ? 'Yes' : 'No'}`);
  }
});

// Output:
// Email field found: email = [email protected]
// Boolean field: isAdmin = No

Avantages :

  • Acces aux cles et valeurs simultanement
  • Syntaxe de destructuring propre
  • Approche la plus flexible
  • Facile a transformer des donnees

Inconvenients :

  • Syntaxe un peu plus complexe
  • Cree des tableaux temporaires pour chaque entree
  • Peut avoir une utilisation memoire legerement plus elevee

Methode 4 : utiliser forEach() avec map et filter

Vous pouvez combiner forEach() avec d'autres methodes de tableaux comme map() et filter() pour creer des pipelines de traitement d'objets plus puissants.

Combiner avec map

const prices = {
  apple: 1.50,
  banana: 0.80,
  orange: 2.00,
  grape: 3.50
};

// Transform object data using map and forEach
const discountedPrices = Object.entries(prices)
  .map(([item, price]) => [item, price * 0.9]) // 10% discount
  .forEach(([item, discountedPrice]) => {
    console.log(`${item}: $${discountedPrice.toFixed(2)} (discounted)`);
  });

// Output:
// apple: $1.35 (discounted)
// banana: $0.72 (discounted)
// orange: $1.80 (discounted)
// grape: $3.15 (discounted)

Combiner avec filter

const inventory = {
  laptop: { stock: 5, price: 999 },
  mouse: { stock: 0, price: 25 },
  keyboard: { stock: 12, price: 75 },
  monitor: { stock: 3, price: 299 },
  cable: { stock: 0, price: 15 }
};

// Filter and process only in-stock items
Object.entries(inventory)
  .filter(([item, data]) => data.stock > 0)
  .forEach(([item, data]) => {
    console.log(`${item}: ${data.stock} in stock at $${data.price}`);
  });

// Output:
// laptop: 5 in stock at $999
// keyboard: 12 in stock at $75
// monitor: 3 in stock at $299

Methode 5 : utiliser forEach() avec des objets imbriques

Quand vous travaillez avec des objets imbriques, vous pouvez utiliser forEach() pour iterer sur plusieurs niveaux de proprietes.

Iterer sur des objets imbriques

const company = {
  name: 'TechCorp',
  departments: {
    engineering: {
      employees: 25,
      budget: 500000
    },
    marketing: {
      employees: 8,
      budget: 150000
    },
    sales: {
      employees: 12,
      budget: 200000
    }
  }
};

// Iterate through nested object structure
Object.entries(company.departments).forEach(([deptName, deptData]) => {
  console.log(`Department: ${deptName}`);
  Object.entries(deptData).forEach(([key, value]) => {
    console.log(`  ${key}: ${value}`);
  });
  console.log('---');
});

// Output:
// Department: engineering
//   employees: 25
//   budget: 500000
// ---
// Department: marketing
//   employees: 8
//   budget: 150000
// ---
// Department: sales
//   employees: 12
//   budget: 200000
// ---

Traitement recursif d'objets

function processObject(obj, prefix = '') {
  Object.entries(obj).forEach(([key, value]) => {
    const fullKey = prefix ? `${prefix}.${key}` : key;
    
    if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
      // Recursively process nested objects
      processObject(value, fullKey);
    } else {
      console.log(`${fullKey}: ${value}`);
    }
  });
}

const config = {
  database: {
    host: 'localhost',
    port: 5432,
    credentials: {
      username: 'admin',
      password: 'secret'
    }
  },
  api: {
    baseUrl: 'https://api.example.com',
    timeout: 5000
  }
};

processObject(config);

// Output:
// database.host: localhost
// database.port: 5432
// database.credentials.username: admin
// database.credentials.password: secret
// api.baseUrl: https://api.example.com
// api.timeout: 5000

Comparaison de performance

Comprendre les caracteristiques de performance des differentes methodes d'iteration d'objets est essentiel pour choisir la bonne approche, surtout avec de gros objets ou des applications sensibles a la performance.

Test de performance

// Performance test function
function performanceTest(method, obj, iterations = 100000) {
  const start = performance.now();
  
  for (let i = 0; i < iterations; i++) {
    method(obj);
  }
  
  const end = performance.now();
  return end - start;
}

// Test object
const testObject = {
  prop1: 'value1', prop2: 'value2', prop3: 'value3', prop4: 'value4', prop5: 'value5',
  prop6: 'value6', prop7: 'value7', prop8: 'value8', prop9: 'value9', prop10: 'value10'
};

// Test methods
const methods = {
  objectKeys: (obj) => {
    Object.keys(obj).forEach(key => {
      const value = obj[key];
    });
  },
  
  objectValues: (obj) => {
    Object.values(obj).forEach(value => {
      // Process value
    });
  },
  
  objectEntries: (obj) => {
    Object.entries(obj).forEach(([key, value]) => {
      // Process key and value
    });
  },
  
  forIn: (obj) => {
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        const value = obj[key];
      }
    }
  }
};

// Run performance tests
console.log('Performance Results (ms):');
Object.entries(methods).forEach(([name, method]) => {
  const time = performanceTest(method, testObject);
  console.log(`${name}: ${time.toFixed(2)}ms`);
});

Classement des performances (resultats typiques)

  1. Boucle for...in : la plus rapide pour une iteration simple
  2. Object.keys() avec forEach : bonnes performances, syntaxe propre
  3. Object.values() avec forEach : bonnes performances quand vous n'avez besoin que des valeurs
  4. Object.entries() avec forEach : un peu plus lent a cause de la creation de tableaux

Exemples concrets

Exemple 1 : traitement de donnees de formulaire

// Form data processing
class FormProcessor {
  constructor() {
    this.validators = {
      email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
      phone: (value) => /^\d{10}$/.test(value),
      age: (value) => value >= 18 && value <= 120,
      name: (value) => value.length >= 2
    };
  }
  
  validateForm(formData) {
    const errors = {};
    
    Object.entries(formData).forEach(([field, value]) => {
      const validator = this.validators[field];
      if (validator && !validator(value)) {
        errors[field] = `${field} is invalid`;
      }
    });
    
    return {
      isValid: Object.keys(errors).length === 0,
      errors
    };
  }
  
  processFormData(formData) {
    const processed = {};
    
    Object.entries(formData).forEach(([key, value]) => {
      // Clean and normalize data
      if (typeof value === 'string') {
        processed[key] = value.trim().toLowerCase();
      } else {
        processed[key] = value;
      }
    });
    
    return processed;
  }
}

// Usage
const formProcessor = new FormProcessor();
const formData = {
  name: 'John Doe',
  email: '[email protected]',
  phone: '1234567890',
  age: 25
};

const validation = formProcessor.validateForm(formData);
console.log('Validation result:', validation);

const processed = formProcessor.processFormData(formData);
console.log('Processed data:', processed);

Exemple 2 : traitement de reponses d'API

// API response processing
class ApiResponseProcessor {
  processUserData(apiResponse) {
    const processedUsers = [];
    
    Object.entries(apiResponse.users).forEach(([userId, userData]) => {
      const processedUser = {
        id: userId,
        ...userData,
        fullName: `${userData.firstName} ${userData.lastName}`,
        isActive: userData.lastLogin > Date.now() - (30 * 24 * 60 * 60 * 1000)
      };
      
      processedUsers.push(processedUser);
    });
    
    return processedUsers;
  }
  
  aggregateStats(data) {
    const stats = {
      totalUsers: 0,
      activeUsers: 0,
      totalRevenue: 0,
      averageAge: 0
    };
    
    let ageSum = 0;
    
    Object.values(data.users).forEach(user => {
      stats.totalUsers++;
      
      if (user.isActive) {
        stats.activeUsers++;
      }
      
      if (user.revenue) {
        stats.totalRevenue += user.revenue;
      }
      
      if (user.age) {
        ageSum += user.age;
      }
    });
    
    stats.averageAge = ageSum / stats.totalUsers;
    
    return stats;
  }
}

// Usage
const processor = new ApiResponseProcessor();
const apiData = {
  users: {
    '1': { firstName: 'John', lastName: 'Doe', age: 30, revenue: 1000, lastLogin: Date.now() - 1000000 },
    '2': { firstName: 'Jane', lastName: 'Smith', age: 25, revenue: 1500, lastLogin: Date.now() - 500000 },
    '3': { firstName: 'Bob', lastName: 'Johnson', age: 35, revenue: 800, lastLogin: Date.now() - 2000000 }
  }
};

const processedUsers = processor.processUserData(apiData);
const stats = processor.aggregateStats(apiData);

console.log('Processed users:', processedUsers);
console.log('Statistics:', stats);

Exemple 3 : gestion de configuration

// Configuration management
class ConfigManager {
  constructor(defaultConfig) {
    this.config = { ...defaultConfig };
  }
  
  updateConfig(updates) {
    Object.entries(updates).forEach(([key, value]) => {
      if (this.config.hasOwnProperty(key)) {
        this.config[key] = value;
        console.log(`Updated ${key} to ${value}`);
      } else {
        console.warn(`Unknown config key: ${key}`);
      }
    });
  }
  
  validateConfig() {
    const errors = [];
    
    Object.entries(this.config).forEach(([key, value]) => {
      if (value === undefined || value === null) {
        errors.push(`${key} is required`);
      }
      
      if (key.includes('Url') && typeof value === 'string' && !value.startsWith('http')) {
        errors.push(`${key} must be a valid URL`);
      }
      
      if (key.includes('Timeout') && (typeof value !== 'number' || value <= 0)) {
        errors.push(`${key} must be a positive number`);
      }
    });
    
    return {
      isValid: errors.length === 0,
      errors
    };
  }
  
  exportConfig() {
    const exportData = {};
    
    Object.entries(this.config).forEach(([key, value]) => {
      // Only export non-sensitive data
      if (!key.toLowerCase().includes('password') && 
          !key.toLowerCase().includes('secret') && 
          !key.toLowerCase().includes('key')) {
        exportData[key] = value;
      }
    });
    
    return exportData;
  }
}

// Usage
const configManager = new ConfigManager({
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3,
  debug: false,
  apiKey: 'secret-key-123'
});

configManager.updateConfig({
  timeout: 10000,
  debug: true,
  newSetting: 'value' // This will show a warning
});

const validation = configManager.validateConfig();
console.log('Config validation:', validation);

const exported = configManager.exportConfig();
console.log('Exported config:', exported);

Pieges courants et comment les eviter

Piege 1 : modifier des objets pendant l'iteration

Probleme : modifier un objet pendant qu'on l'itere peut mener a un comportement inattendu.

const data = {
  a: 1,
  b: 2,
  c: 3
};

// This can cause issues
Object.keys(data).forEach(key => {
  if (data[key] > 1) {
    delete data[key]; // Modifying during iteration
  }
});

console.log(data); // May not behave as expected

Solution : collectez d'abord les cles a modifier, puis modifiez-les separement.

const data = {
  a: 1,
  b: 2,
  c: 3
};

// Safe approach: collect keys first
const keysToDelete = [];
Object.keys(data).forEach(key => {
  if (data[key] > 1) {
    keysToDelete.push(key);
  }
});

// Then delete them
keysToDelete.forEach(key => {
  delete data[key];
});

console.log(data); // { a: 1 }

Piege 2 : ne pas gerer les objets imbriques

Probleme : iterer sur des objets imbriques sans les gerer correctement.

const data = {
  user: {
    name: 'John',
    age: 30
  },
  settings: {
    theme: 'dark'
  }
};

// This only processes the top level
Object.entries(data).forEach(([key, value]) => {
  console.log(key, value); // value is an object, not a primitive
});

Solution : testez les types d'objets et traitez-les de facon appropriee.

const data = {
  user: {
    name: 'John',
    age: 30
  },
  settings: {
    theme: 'dark'
  }
};

// Proper handling of nested objects
Object.entries(data).forEach(([key, value]) => {
  if (typeof value === 'object' && value !== null) {
    console.log(`${key}:`);
    Object.entries(value).forEach(([nestedKey, nestedValue]) => {
      console.log(`  ${nestedKey}: ${nestedValue}`);
    });
  } else {
    console.log(`${key}: ${value}`);
  }
});

Piege 3 : problemes de performance avec de gros objets

Probleme : utiliser forEach sur de tres gros objets peut provoquer des problemes de performance.

// This can be slow for very large objects
const largeObject = {}; // Assume this has thousands of properties
Object.entries(largeObject).forEach(([key, value]) => {
  // Processing each entry
});

Solution : utilisez des methodes d'iteration plus efficaces ou un traitement par batch.

// More efficient approach for large objects
async function processLargeObject(obj, batchSize = 100) {
  const entries = Object.entries(obj);
  
  for (let i = 0; i < entries.length; i += batchSize) {
    const batch = entries.slice(i, i + batchSize);
    
    batch.forEach(([key, value]) => {
      // Process each entry in the batch
    });
    
    // Allow other operations to run
    if (i + batchSize < entries.length) {
      // Use setTimeout to yield control
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
}

Bonnes pratiques et recommandations

Quand utiliser chaque methode

  • Utilisez Object.keys() avec forEach : quand vous avez besoin des cles et des valeurs et que vous voulez une syntaxe propre
  • Utilisez Object.values() avec forEach : quand vous ne voulez traiter que des valeurs et pas les cles
  • Utilisez Object.entries() avec forEach : quand vous avez besoin des cles et des valeurs avec destructuring
  • Utilisez une boucle for...in : quand vous avez besoin d'un maximum de performance pour une iteration simple

Regles de performance

  1. Pour de petits objets (< 100 proprietes) : utilisez n'importe quelle methode selon la lisibilite
  2. Pour des objets moyens (100-1000 proprietes) : pensez a Object.keys() ou for...in
  3. Pour de gros objets (> 1000 proprietes) : utilisez une boucle for...in ou un traitement par batch
  4. Pour des iterations frequentes : mettez en cache les resultats de Object.keys()/Object.values()/Object.entries()

Conseils de qualite de code

  • Utilisez toujours const pour les variables d'iteration pour eviter des reassignations accidentelles
  • Utilisez des noms de variables explicites dans le destructuring : ([key, value]) plutot que ([k, v])
  • Gerez les cas limites comme null/undefined
  • Pensez a utiliser des early returns ou continue pour une meilleure lisibilite
  • Ajoutez des commentaires pour la logique d'iteration complexe

Compatibilite navigateur

Toutes les methodes vues dans ce guide ont un excellent support navigateur :

  • Object.keys() : tous les navigateurs modernes (IE9+)
  • Object.values() : tous les navigateurs modernes (Chrome 54+, Firefox 47+, Safari 10.1+)
  • Object.entries() : tous les navigateurs modernes (Chrome 54+, Firefox 47+, Safari 10.1+)
  • forEach() : tous les navigateurs modernes (IE9+)
  • Destructuring assignment : tous les navigateurs modernes (Chrome 49+, Firefox 41+, Safari 8+)

Polyfills pour les anciens navigateurs

// Polyfill for Object.values
if (!Object.values) {
  Object.values = function(obj) {
    return Object.keys(obj).map(key => obj[key]);
  };
}

// Polyfill for Object.entries
if (!Object.entries) {
  Object.entries = function(obj) {
    return Object.keys(obj).map(key => [key, obj[key]]);
  };
}

Alternatives modernes

Utiliser for...of avec Object.entries()

const person = {
  name: 'John',
  age: 30,
  city: 'New York'
};

// Modern for...of loop with Object.entries()
for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

// This is often more readable than forEach for simple cases

Utiliser Map pour de meilleures performances

// Convert object to Map for better iteration performance
const personMap = new Map([
  ['name', 'John'],
  ['age', 30],
  ['city', 'New York']
]);

// Map has built-in forEach method
personMap.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});

// Or use for...of with Map
for (const [key, value] of personMap) {
  console.log(`${key}: ${value}`);
}

Conclusion

Utiliser forEach() avec des objets JavaScript est une technique puissante qui permet d'iterer sur des proprietes d'objet de facon propre et fonctionnelle. En combinant Object.keys(), Object.values() et Object.entries() avec forEach(), vous pouvez traiter des donnees d'objet efficacement et de facon maintenable.

La cle pour choisir la bonne methode est de comprendre vos besoins : avez-vous besoin des cles, des valeurs, ou des deux ? Travaillez-vous avec de gros objets qui necessitent une optimisation de performance ? Devez-vous gerer des structures imbriquees ? En tenant compte de ces facteurs et en suivant les bonnes pratiques de ce guide, vous pouvez ecrire du code JavaScript efficace et lisible pour l'iteration d'objets.

Gardez en tete que, meme si forEach() est excellent pour les effets de bord et le traitement, d'autres methodes comme map(), filter() et reduce() peuvent etre plus appropriees quand vous devez transformer ou agreger des donnees. La combinaison de ces methodes avec des techniques d'iteration d'objets fournit une boite a outils complete pour travailler avec des objets JavaScript dans des applications modernes.

Que vous construisiez des applications web simples ou des systemes complexes de traitement de donnees, maitriser l'iteration d'objets avec forEach() vous aidera a ecrire du code JavaScript plus propre, plus efficace et plus maintenable. Les exemples et patterns de ce guide constituent une base solide pour votre parcours de developpement JavaScript.

Commencez à créer avec Axentix

Prêt à créer des sites web exceptionnels ? Commencez avec le framework Axentix dès aujourd'hui.

Commencer

Articles similaires