وحدات ES
التنظيم المعياري للشيفرة
10-15 دقيقة
JavaScript الحديث: وحدات ES
وحدات ES (ECMAScript) هي نظام موحَّد لتنظيم شيفرة JavaScript ومشاركتها بين ملفات مختلفة. وهي تتيح إنشاء تطبيقات أكثر قابلية للصيانة وأكثر معيارية.
مقدمة إلى وحدات ES
لماذا نستخدم الوحدات؟
- التغليف (Encapsulation): لكل وحدة نطاقها (scope) الخاص
- قابلية إعادة الاستخدام: يمكن مشاركة الشيفرة بسهولة بين الملفات
- تبعيات صريحة: العلاقات بين الوحدات محددة بوضوح
- التنظيم: بنية واضحة للشيفرة
الإعداد الأساسي
<!-- في ملف HTML الخاص بك -->
<script type="module" src="main.js"></script>
// في ملف package.json الخاص بك
{
"type": "module"
}
تصدير الوحدات
التصدير المسمّى
// mathematiques.js
export const PI = 3.14159;
export function addition(a, b) {
return a + b;
}
export function soustraction(a, b) {
return a - b;
}
// بديل: تصدير مجمّع
const multiplication = (a, b) => a * b;
const division = (a, b) => a / b;
export { multiplication, division };
التصدير الافتراضي
// calculateur.js
class Calculateur {
addition(a, b) {
return a + b;
}
soustraction(a, b) {
return a - b;
}
}
export default Calculateur;
الجمع بين أنواع التصدير
// utils.js
export const VERSION = "1.0.0";
export function formaterNombre(nombre) {
return nombre.toFixed(2);
}
const Utils = {
VERSION,
formaterNombre
};
export default Utils;
استيراد الوحدات
الاستيراد المسمّى
// app.js
import { addition, soustraction } from './mathematiques.js';
import { PI as MATH_PI } from './mathematiques.js';
console.log(addition(2, 3)); // 5
console.log(MATH_PI); // 3.14159
الاستيراد الافتراضي
// app.js
import Calculateur from './calculateur.js';
const calc = new Calculateur();
console.log(calc.addition(5, 3)); // 8
الاستيراد المدمج
// app.js
import Utils, { VERSION } from './utils.js';
// أو
import Utils, * as UtilsAll from './utils.js';
console.log(Utils.formaterNombre(3.14159)); // "3.14"
console.log(VERSION); // "1.0.0"
الاستيراد الديناميكي
// app.js
async function chargerModule() {
try {
const module = await import('./grand-module.js');
console.log(module.fonctionImportante());
} catch (erreur) {
console.error('Erreur de chargement:', erreur);
}
}
تنظيم الوحدات
بنية مشروع نموذجية
src/
├── index.js
├── modules/
│ ├── utilisateurs/
│ │ ├── index.js
│ │ ├── utilisateur.js
│ │ └── validation.js
│ ├── produits/
│ │ ├── index.js
│ │ └── produit.js
│ └── utils/
│ ├── index.js
│ ├── format.js
│ └── validation.js
وحدات التجميع (Barrel modules)
// modules/utilisateurs/index.js
export { default as Utilisateur } from './utilisateur.js';
export { validerEmail, validerMotDePasse } from './validation.js';
// app.js
import { Utilisateur, validerEmail } from './modules/utilisateurs';
الوحدات المُجمِّعة
// modules/index.js
export * from './utilisateurs';
export * from './produits';
export * from './utils';
// app.js
import { Utilisateur, Produit, formaterPrix } from './modules';
الأنماط وأفضل الممارسات
نمط المفرد (Singleton)
// config.js
class Config {
constructor() {
this.api = 'https://api.example.com';
this.timeout = 5000;
}
setApi(url) {
this.api = url;
}
}
export default new Config();
نمط المصنع (Factory)
// loggerFactory.js
const createLogger = (prefix = '') => ({
log: (message) => console.log(`${prefix}${message}`),
error: (message) => console.error(`${prefix}${message}`),
warn: (message) => console.warn(`${prefix}${message}`)
});
export default createLogger;
نمط المحوِّل (Adapter)
// apiAdapter.js
import axios from 'axios';
import { formatResponse } from './utils.js';
export const api = {
async get(url) {
const response = await axios.get(url);
return formatResponse(response.data);
},
async post(url, data) {
const response = await axios.post(url, data);
return formatResponse(response.data);
}
};
حالات استخدام عملية
تطبيق معياري
// modules/auth/auth.service.js
export class AuthService {
async login(credentials) {
// منطق المصادقة
}
async logout() {
// منطق تسجيل الخروج
}
}
// modules/auth/index.js
export { AuthService } from './auth.service.js';
// app.js
import { AuthService } from './modules/auth';
const auth = new AuthService();
إدارة الحالة بشكل معياري
// store/counter.js
let count = 0;
const listeners = new Set();
export const getCount = () => count;
export const increment = () => {
count++;
notifyListeners();
};
export const decrement = () => {
count--;
notifyListeners();
};
export const subscribe = (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
};
function notifyListeners() {
listeners.forEach(listener => listener(count));
}
// app.js
import { getCount, increment, subscribe } from './store/counter.js';
subscribe((newCount) => {
console.log('Nouveau compte:', newCount);
});
الإضافات المعيارية (Plugins)
// plugins/logger.js
export const loggerPlugin = {
install(app) {
app.logger = {
log: console.log,
error: console.error
};
}
};
// plugins/storage.js
export const storagePlugin = {
install(app) {
app.storage = {
get: key => localStorage.getItem(key),
set: (key, value) => localStorage.setItem(key, value)
};
}
};
// app.js
import { loggerPlugin } from './plugins/logger.js';
import { storagePlugin } from './plugins/storage.js';
class App {
constructor() {
this.plugins = new Map();
}
use(plugin) {
plugin.install(this);
this.plugins.set(plugin.name, plugin);
}
}
const app = new App();
app.use(loggerPlugin);
app.use(storagePlugin);
تمارين عملية
- إنشاء مكتبة أدوات مساعدة
// utils/string.js
export const capitalize = (str) =>
str.charAt(0).toUpperCase() + str.slice(1);
export const reverse = (str) =>
str.split('').reverse().join('');
// utils/array.js
export const unique = (arr) =>
[...new Set(arr)];
export const groupBy = (arr, key) =>
arr.reduce((acc, item) => {
(acc[item[key]] = acc[item[key]] || []).push(item);
return acc;
}, {});
// utils/index.js
export * from './string.js';
export * from './array.js';
// app.js
import { capitalize, unique } from './utils';
console.log(capitalize('hello')); // "Hello"
console.log(unique([1, 2, 2, 3])); // [1, 2, 3]
- نظام إضافات معياري
// plugin-system.js
export class PluginSystem {
constructor() {
this.plugins = new Map();
}
register(name, plugin) {
if (this.plugins.has(name)) {
throw new Error(`Plugin ${name} déjà enregistré`);
}
this.plugins.set(name, plugin);
if (plugin.install) {
plugin.install(this);
}
}
use(name) {
const plugin = this.plugins.get(name);
if (!plugin) {
throw new Error(`Plugin ${name} non trouvé`);
}
return plugin;
}
}
// plugins/validation.js
export const validationPlugin = {
name: 'validation',
install(system) {
system.validate = {
email: (email) => /^.+@.+\..+$/.test(email),
required: (value) => value !== undefined && value !== ''
};
}
};
// app.js
import { PluginSystem } from './plugin-system.js';
import { validationPlugin } from './plugins/validation.js';
const system = new PluginSystem();
system.register(validationPlugin.name, validationPlugin);
console.log(system.validate.email('test@example.com')); // true
الخاتمة
وحدات ES أداة أساسية من أجل:
- تنظيم الشيفرة بطريقة معيارية وقابلة للصيانة
- إدارة التبعيات بين مختلف أجزاء التطبيق بفعالية
- إنشاء أنظمة قابلة للتوسعة عبر الإضافات (plugins)
- تحسين قابلية إعادة استخدام الشيفرة
في الدرس التالي، سنستكشف هياكل البيانات الحديثة مثل Map و Set.
هل تحتاج إلى مراجعة Promises و async/await؟ اطّلع على الدرس السابق حول البرمجة غير المتزامنة.
التعليقات
التعليقات مدعومة عبر GitHub Discussions
سجّل الدخول باستخدام GitHub للمشاركة في النقاش