JavaScript Modern

Écrivez du JavaScript ES6+ moderne en suivant les meilleures pratiques pour la performance, la sécurité et la maintenabilité. Utilisez pour coder en JS, corriger des bugs ou implémenter des fonctionnalités frontend.

Spar Skills Guide Bot
DeveloppementIntermédiaire1 vues0 installations28/02/2026
Claude CodeCursorWindsurfCopilotCodex
javascriptes6-plusfrontendasync-awaitbest-practices

name: javascript description: Write modern JavaScript/ES6+ code following best practices for performance, security, and maintainability. Use when writing JS code, fixing bugs, or implementing frontend functionality.

JavaScript Skill

Instructions

When writing JavaScript:

1. Modern Syntax

// Use const by default, let when needed
const API_URL = 'https://api.example.com';
let count = 0;

// Arrow functions
const add = (a, b) => a + b;
const greet = name => `Hello, ${name}!`;

// Destructuring
const { name, email } = user;
const [first, second, ...rest] = items;

// Spread operator
const newArray = [...oldArray, newItem];
const newObject = { ...oldObject, newProp: value };

// Template literals
const message = `User ${name} has ${count} items`;

// Optional chaining
const city = user?.address?.city;

// Nullish coalescing
const value = input ?? defaultValue;

2. Async/Await

// Async function
async function fetchData(url) {
  try {
    const response = await fetch(url);

    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
    throw error;
  }
}

// Parallel requests
async function fetchAll(urls) {
  const promises = urls.map(url => fetch(url));
  const responses = await Promise.all(promises);
  return Promise.all(responses.map(r => r.json()));
}

// With timeout
async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);

  try {
    const response = await fetch(url, { signal: controller.signal });
    return await response.json();
  } finally {
    clearTimeout(timeoutId);
  }
}

3. Array Methods

const users = [
  { id: 1, name: 'Alice', age: 25, active: true },
  { id: 2, name: 'Bob', age: 30, active: false },
  { id: 3, name: 'Charlie', age: 35, active: true },
];

// map - transform items
const names = users.map(user => user.name);

// filter - select items
const activeUsers = users.filter(user => user.active);

// find - get first match
const bob = users.find(user => user.name === 'Bob');

// some/every - check conditions
const hasActive = users.some(user => user.active);
const allActive = users.every(user => user.active);

// reduce - aggregate
const totalAge = users.reduce((sum, user) => sum + user.age, 0);

// Chaining
const activeNames = users
  .filter(user => user.active)
  .map(user => user.name)
  .sort();

4. DOM Manipulation

// Selecting elements
const element = document.querySelector('.class');
const elements = document.querySelectorAll('.class');

// Creating elements
const div = document.createElement('div');
div.className = 'card';
div.innerHTML = `
  <h2>${title}</h2>
  <p>${description}</p>
`;

// Event handling
element.addEventListener('click', (event) => {
  event.preventDefault();
  // Handle click
});

// Event delegation
document.querySelector('.list').addEventListener('click', (event) => {
  if (event.target.matches('.item')) {
    handleItemClick(event.target);
  }
});

// IntersectionObserver (lazy loading, animations)
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.1 });

document.querySelectorAll('.animate').forEach(el => observer.observe(el));

5. Classes

class User {
  #privateField; // Private field

  constructor(name, email) {
    this.name = name;
    this.email = email;
    this.#privateField = 'secret';
  }

  // Getter
  get displayName() {
    return this.name.toUpperCase();
  }

  // Setter
  set displayName(value) {
    this.name = value.trim();
  }

  // Method
  greet() {
    return `Hello, I'm ${this.name}`;
  }

  // Static method
  static create(data) {
    return new User(data.name, data.email);
  }
}

// Inheritance
class Admin extends User {
  constructor(name, email, role) {
    super(name, email);
    this.role = role;
  }

  greet() {
    return `${super.greet()} and I'm an ${this.role}`;
  }
}

6. Modules

// Named exports
export const API_URL = 'https://api.example.com';
export function fetchData() { /* ... */ }
export class User { /* ... */ }

// Default export
export default function main() { /* ... */ }

// Importing
import main, { API_URL, fetchData, User } from './module.js';

// Dynamic import
const module = await import('./heavy-module.js');

7. Error Handling

// Custom error
class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = 'ValidationError';
    this.field = field;
  }
}

// Try-catch with specific handling
try {
  await submitForm(data);
} catch (error) {
  if (error instanceof ValidationError) {
    showFieldError(error.field, error.message);
  } else if (error instanceof NetworkError) {
    showToast('Network error. Please try again.');
  } else {
    console.error('Unexpected error:', error);
    showToast('Something went wrong.');
  }
}

8. Local Storage

// Store data
const saveData = (key, data) => {
  localStorage.setItem(key, JSON.stringify(data));
};

// Retrieve data
const getData = (key, defaultValue = null) => {
  const stored = localStorage.getItem(key);
  return stored ? JSON.parse(stored) : defaultValue;
};

// Remove data
const removeData = (key) => {
  localStorage.removeItem(key);
};

9. Debounce & Throttle

// Debounce - wait until stopped
function debounce(func, delay) {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func(...args), delay);
  };
}

const debouncedSearch = debounce((query) => {
  fetchResults(query);
}, 300);

// Throttle - limit frequency
function throttle(func, limit) {
  let inThrottle;
  return (...args) => {
    if (!inThrottle) {
      func(...args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

const throttledScroll = throttle(() => {
  updatePosition();
}, 100);

10. Best Practices

  • Use const by default
  • Prefer arrow functions
  • Use async/await over callbacks
  • Handle all errors
  • Avoid global variables
  • Use meaningful variable names
  • Keep functions small and focused
  • Comment complex logic
  • Use strict equality (===)
  • Validate user input

WordPress-Specific JavaScript

11. Enqueueing Scripts Properly

function theme_enqueue_scripts() {
    // Frontend script
    wp_enqueue_script(
        'theme-main',
        get_template_directory_uri() . '/assets/js/main.js',
        array(), // dependencies
        '1.0.0',
        true // in footer
    );

    // With jQuery dependency
    wp_enqueue_script(
        'theme-jquery-script',
        get_template_directory_uri() . '/assets/js/custom.js',
        array('jquery'),
        '1.0.0',
        true
    );

    // Pass PHP data to JavaScript
    wp_localize_script('theme-main', 'themeData', array(
        'ajaxUrl' => admin_url('admin-ajax.php'),
        'restUrl' => rest_url('theme/v1/'),
        'nonce'   => wp_create_nonce('theme_nonce'),
        'i18n'    => array(
            'loading' => __('Loading...', 'theme'),
            'error'   => __('An error occurred', 'theme'),
        ),
    ));
}
add_action('wp_enqueue_scripts', 'theme_enqueue_scripts');

12. AJAX with admin-ajax.php

// Frontend JavaScript
async function submitForm(formData) {
    const data = new FormData();
    data.append('action', 'theme_submit_form');
    data.append('nonce', themeData.nonce);
    data.append('name', formData.name);
    data.append('email', formData.email);

    try {
        const response = await fetch(themeData.ajaxUrl, {
            method: 'POST',
            body: data,
            credentials: 'same-origin',
        });

        const result = await response.json();

        if (result.success) {
            return result.data;
        } else {
            throw new Error(result.data.message || 'Request failed');
        }
    } catch (error) {
        console.error('AJAX Error:', error);
        throw error;
    }
}
// PHP handler
add_action('wp_ajax_theme_submit_form', 'theme_handle_form');
add_action('wp_ajax_nopriv_theme_submit_form', 'theme_handle_form');

function theme_handle_form() {
    // Verify nonce
    if (!wp_verify_nonce($_POST['nonce'], 'theme_nonce')) {
        wp_send_json_error(array('message' => 'Invalid nonce'));
    }

    // Sanitize input
    $name = sanitize_text_field($_POST['name']);
    $email = sanitize_email($_POST['email']);

    // Process...

    wp_send_json_success(array('message' => 'Form submitted'));
}

13. REST API Requests

// GET request
async function getPosts() {
    const response = await fetch(`${themeData.restUrl}posts`, {
        headers: {
            'X-WP-Nonce': themeData.nonce,
        },
    });
    return response.json();
}

// POST request
async function createPost(data) {
    const response = await fetch(`${themeData.restUrl}posts`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-WP-Nonce': themeData.nonce,
        },
        body: JSON.stringify(data),
    });
    return response.json();
}

// Using wp.apiFetch (Gutenberg)
wp.apiFetch({ path: '/wp/v2/posts' }).then(posts => {
    console.log(posts);
});

14. jQuery Compatibility

// WordPress jQuery no-conflict wrapper
(function($) {
    $(document).ready(function() {
        // Your jQuery code here
        $('.element').on('click', function() {
            $(this).toggleClass('active');
        });
    });
})(jQuery);

// Or with modern syntax
jQuery(($) => {
    $('.element').on('click', function() {
        $(this).toggleClass('active');
    });
});

15. Gutenberg/Block Editor JavaScript

// Using wp.data for state
const { select, dispatch } = wp.data;

// Get current post
const post = select('core/editor').getCurrentPost();

// Get blocks
const blocks = select('core/block-editor').getBlocks();

// Using wp.hooks for filters
wp.hooks.addFilter(
    'blocks.registerBlockType',
    'theme/modify-block',
    (settings, name) => {
        if (name === 'core/paragraph') {
            settings.attributes.customAttr = {
                type: 'string',
                default: '',
            };
        }
        return settings;
    }
);

// Using wp.i18n for translations
const { __, _n, sprintf } = wp.i18n;
const message = __('Hello World', 'theme');
const items = sprintf(_n('%d item', '%d items', count, 'theme'), count);

16. WordPress JavaScript Best Practices

  • Always use nonces for security in AJAX/REST requests
  • Use wp_localize_script() to pass data from PHP to JS
  • Wrap jQuery code in no-conflict wrapper
  • Prefer REST API over admin-ajax for new projects
  • Use wp.apiFetch in Gutenberg context
  • Namespace your code to avoid conflicts
  • Load scripts in footer when possible (true as last param)
  • Use dependencies array correctly (e.g., array('jquery', 'wp-element'))
  • Handle errors gracefully with user-friendly messages
  • Test in both frontend and admin contexts

Skills similaires