JavaScript المتقدم (ES6+)

وحدات ES6

13 دقيقة الدرس 32 من 40

وحدات ES6

مرحباً بك في عالم وحدات ES6! في هذا الدرس، سنستكشف نظام الوحدات الأصلي في JavaScript الذي يسمح لك بتنظيم وإعادة استخدام الكود عبر الملفات. الوحدات ضرورية لبناء تطبيقات قابلة للتوسع وسهلة الصيانة.

ما هي وحدات ES6؟

توفر وحدات ES6 طريقة موحدة لتنظيم كود JavaScript في ملفات منفصلة واستيراد/تصدير الوظائف بينها:

الفوائد الرئيسية: تتيح الوحدات إعادة استخدام الكود، وتنظيم أفضل، وإدارة مساحات الأسماء، وإدارة التبعيات. كل وحدة لها نطاقها الخاص، مما يمنع تلوث مساحة الأسماء العامة.

بناء جملة التصدير الأساسي

هناك طريقتان للتصدير من وحدة: التصديرات المسماة والتصديرات الافتراضية.

التصديرات المسماة (math.js): // تصدير عناصر فردية export const PI = 3.14159; export const E = 2.71828; export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; } // أو تصدير عناصر متعددة دفعة واحدة const subtract = (a, b) => a - b; const divide = (a, b) => a / b; export { subtract, divide };

التصديرات الافتراضية

يمكن أن يكون لكل وحدة تصدير افتراضي واحد، يُستخدم عادةً للوظيفة الرئيسية للوحدة:

التصدير الافتراضي (calculator.js): // الخيار 1: تصدير افتراضي مباشر export default class Calculator { add(a, b) { return a + b; } subtract(a, b) { return a - b; } } // الخيار 2: تصدير افتراضي منفصل class Calculator { // ... } export default Calculator; // الخيار 3: تصدير افتراضي مجهول export default function(a, b) { return a + b; }
أفضل الممارسات: استخدم التصديرات المسماة عند تصدير عناصر متعددة، والتصديرات الافتراضية للتصدير الأساسي للوحدة. يمكنك الجمع بين كليهما في نفس الملف.

بناء جملة الاستيراد الأساسي

استورد الوظائف من وحدات أخرى باستخدام الكلمة المفتاحية import:

استيراد التصديرات المسماة: // استيراد تصديرات مسماة محددة import { add, multiply } from './math.js'; console.log(add(5, 3)); // 8 console.log(multiply(4, 2)); // 8 // استيراد جميع التصديرات المسماة ككائن import * as math from './math.js'; console.log(math.add(5, 3)); // 8 console.log(math.PI); // 3.14159 console.log(math.multiply(4, 2)); // 8
استيراد التصديرات الافتراضية: // استيراد التصدير الافتراضي (يمكن أن يكون الاسم أي شيء) import Calculator from './calculator.js'; const calc = new Calculator(); console.log(calc.add(10, 5)); // 15 // استيراد كل من التصديرات الافتراضية والمسماة import Calculator, { PI, E } from './calculator.js';

إعادة تسمية الاستيرادات والتصديرات

استخدم الكلمة المفتاحية as لإعادة تسمية الاستيرادات أو التصديرات:

إعادة تسمية التصديرات: // utils.js const calculate = (a, b) => a + b; const format = (str) => str.toUpperCase(); export { calculate as sum, format as uppercase };
إعادة تسمية الاستيرادات: // app.js import { sum as add, uppercase as toUpper } from './utils.js'; console.log(add(5, 3)); // 8 console.log(toUpper('hello')); // HELLO // مفيد لتجنب تعارضات التسمية import { format as formatString } from './string-utils.js'; import { format as formatNumber } from './number-utils.js';

إعادة التصدير من الوحدات

أنشئ ملف فهرس يجمع ويعيد التصدير من وحدات متعددة:

نمط إعادة التصدير (utils/index.js): // إعادة تصدير كل شيء من وحدات أخرى export * from './math.js'; export * from './string.js'; // إعادة تصدير تصديرات مسماة محددة export { add, subtract } from './math.js'; export { capitalize } from './string.js'; // إعادة التصدير وإعادة التسمية export { multiply as times } from './math.js'; // إعادة تصدير الافتراضي كمسمى export { default as Calculator } from './calculator.js';
استخدام الوحدات المُعاد تصديرها: // الآن يمكنك الاستيراد من ملف واحد import { add, subtract, capitalize, Calculator } from './utils/index.js'; // أو استخدام اسم الدليل (إذا كان index.js موجوداً) import { add, subtract } from './utils';

الاستيرادات الديناميكية

حمّل الوحدات ديناميكياً في وقت التشغيل باستخدام دالة import()، والتي تُرجع Promise:

بناء جملة الاستيراد الديناميكي: // تحميل وحدة عند الطلب async function loadCalculator() { const module = await import('./calculator.js'); const Calculator = module.default; const calc = new Calculator(); console.log(calc.add(5, 3)); // 8 } // مع التصديرات المسماة async function loadMath() { const { add, multiply } = await import('./math.js'); console.log(add(2, 3)); // 5 console.log(multiply(4, 5)); // 20 } // التحميل الشرطي if (condition) { import('./heavy-module.js').then(module => { module.doSomething(); }); }
حالة الاستخدام: الاستيرادات الديناميكية مثالية لتقسيم الكود، والتحميل البطيء للميزات، وتحميل الوحدات بناءً على إجراءات المستخدم أو الشروط.

نطاق وسلوك الوحدة

فهم كيفية تصرف الوحدات أمر بالغ الأهمية للاستخدام الفعال:

خصائص الوحدات: 1. للوحدات نطاقها الخاص - المتغيرات ليست عامة - 'this' على المستوى الأعلى غير محدد 2. الوحدات هي singletons - يتم تنفيذ الكود مرة واحدة عند أول استيراد - يتم مشاركة نفس المثيل عبر الاستيرادات 3. الاستيرادات للقراءة فقط - لا يمكن إعادة تعيين الارتباطات المستوردة - لكن يمكن تعديل خصائص الكائن 4. الوحدات مؤجلة - مشابه للنصوص مع سمة defer - يتم التنفيذ بعد تحليل HTML
مثال - Singleton للوحدة: // counter.js let count = 0; export function increment() { return ++count; } export function getCount() { return count; } // app.js import { increment, getCount } from './counter.js'; console.log(increment()); // 1 console.log(increment()); // 2 // another-file.js import { getCount } from './counter.js'; console.log(getCount()); // 2 (نفس العدد، حالة مشتركة)

أنماط الوحدات وأفضل الممارسات

إليك أنماط شائعة لتنظيم الوحدات:

1. فئة واحدة لكل ملف (user.js): export default class User { constructor(name, email) { this.name = name; this.email = email; } getInfo() { return `${this.name} (${this.email})`; } } 2. دوال ذات صلة (array-utils.js): export function chunk(array, size) { // التنفيذ } export function flatten(array) { // التنفيذ } export function unique(array) { return [...new Set(array)]; } 3. كائن التكوين (config.js): export default { apiUrl: 'https://api.example.com', timeout: 5000, retries: 3 }; 4. نمط المصنع (create-logger.js): export default function createLogger(name) { return { log: (msg) => console.log(`[${name}] ${msg}`), error: (msg) => console.error(`[${name}] ${msg}`) }; }
مهم: يجب تقديم ملفات الوحدات بنوع MIME الصحيح (text/javascript) وتتطلب خادم ويب. لن تعمل مع بروتوكول file:// في معظم المتصفحات.

استخدام الوحدات في HTML

قم بتضمين الوحدات في HTML الخاص بك باستخدام سمة type="module":

استخدام وحدة HTML: <!DOCTYPE html> <html> <head> <title>مثال على وحدات ES6</title> </head> <body> <h1>عرض توضيحي للوحدة</h1> <!-- نقطة الدخول الرئيسية للوحدة --> <script type="module" src="app.js"></script> <!-- وحدة مضمنة --> <script type="module"> import { add } from './math.js'; console.log(add(2, 3)); </script> <!-- بديل للمتصفحات بدون دعم الوحدات --> <script nomodule src="bundle.js"></script> </body> </html>

مثال من العالم الحقيقي: نظام إدارة المستخدمين

لنبني مثالاً عملياً بوحدات متعددة:

models/user.js: export default class User { constructor(id, name, email) { this.id = id; this.name = name; this.email = email; } toString() { return `User #${this.id}: ${this.name}`; } } services/user-service.js: import User from '../models/user.js'; class UserService { constructor() { this.users = []; this.nextId = 1; } create(name, email) { const user = new User(this.nextId++, name, email); this.users.push(user); return user; } findById(id) { return this.users.find(u => u.id === id); } getAll() { return [...this.users]; } } export default new UserService(); // تصدير مثيل singleton utils/validator.js: export function validateEmail(email) { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return regex.test(email); } export function validateName(name) { return name && name.length >= 2; } app.js: import userService from './services/user-service.js'; import { validateEmail, validateName } from './utils/validator.js'; function createUser(name, email) { if (!validateName(name)) { throw new Error('Invalid name'); } if (!validateEmail(email)) { throw new Error('Invalid email'); } return userService.create(name, email); } // الاستخدام try { const user1 = createUser('John Doe', 'john@example.com'); const user2 = createUser('Jane Smith', 'jane@example.com'); console.log(userService.getAll()); console.log(userService.findById(1)); // John Doe } catch (error) { console.error(error.message); }

تمرين تطبيقي:

المهمة: أنشئ نظام سلة تسوق معياري مع:

  1. فئة Product في models/product.js
  2. فئة Cart في services/cart.js مع دوال add و remove و getTotal
  3. دوال مساعدة في utils/currency.js لتنسيق الأسعار
  4. ملف app.js يستخدم جميع الوحدات

الحل:

models/product.js: export default class Product { constructor(id, name, price) { this.id = id; this.name = name; this.price = price; } } services/cart.js: export default class Cart { constructor() { this.items = []; } add(product, quantity = 1) { const existingItem = this.items.find(i => i.product.id === product.id); if (existingItem) { existingItem.quantity += quantity; } else { this.items.push({ product, quantity }); } } remove(productId) { this.items = this.items.filter(i => i.product.id !== productId); } getTotal() { return this.items.reduce((sum, item) => { return sum + (item.product.price * item.quantity); }, 0); } getItems() { return [...this.items]; } } utils/currency.js: export function formatPrice(amount, currency = 'USD') { return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount); } app.js: import Product from './models/product.js'; import Cart from './services/cart.js'; import { formatPrice } from './utils/currency.js'; const cart = new Cart(); const laptop = new Product(1, 'Laptop', 999.99); const mouse = new Product(2, 'Mouse', 29.99); cart.add(laptop, 1); cart.add(mouse, 2); console.log('Cart Items:', cart.getItems()); console.log('Total:', formatPrice(cart.getTotal())); // $1,059.97

الملخص

في هذا الدرس، تعلمت:

  • وحدات ES6 توفر طريقة أصلية لتنظيم الكود في ملفات منفصلة
  • استخدم التصديرات المسماة للتصديرات المتعددة والتصديرات الافتراضية للتصديرات الرئيسية
  • استورد الوحدات باستخدام عبارات import مع خيارات بناء جملة متنوعة
  • الاستيرادات الديناميكية تسمح بتحميل الوحدات عند الطلب باستخدام import()
  • للوحدات نطاقها الخاص وهي مثيلات singleton
  • استخدم type="module" في علامات HTML script لاستخدام وحدات ES6
  • أنماط الوحدات تساعد في تنظيم الكود للتوسع والصيانة
التالي: في الدرس التالي، سنستكشف Module Bundlers مثل Webpack ونفهم كيفية تحسين التطبيقات القائمة على الوحدات!