التغليف في لغة JavaScript
يا هلا! تعال نتكلم عن التغليف (Encapsulation) في جافاسكريبت. الموضوع بسيط وحلو، وفكرته الأساسية إنك تخبي تفاصيل الشغل الداخلي للكائن (Object) وتخلي بس الواجهة اللي الناس تحتاج تتعامل معاها ظاهرة.
ليش نحتاج التغليف؟
- حماية البيانات: ما تخلي أي حد يغير بيانات مهمة بالغلط.
- المرونة: لو غيرت طريقة الشغل الداخلية، ما يحتاج تعدل كل مكان يستخدم الكائن حقك، بس الواجهة هي اللي تهم.
- تنظيم الكود: يخلي الكود أنظف وأسهل للفهم والصيانة.
كيف نسوي تغليف في جافاسكريبت؟
في كم طريقة، بعضها قديم وبعضها جديد:
1. باستخدام الـ Closures (الطريقة الكلاسيكية)
هذي طريقة قديمة لكن قوية. الفكرة إنك تسوي دالة ترجع أوبجكت، وكل المتغيرات اللي داخل الدالة تكون "خاصة" (private) وما تقدر توصلها من برا إلا عن طريق الدوال (methods) اللي ترجعها الدالة الأم.
function createCounter() {
let count = 0; // هذا متغير خاص (private)
return {
increment: function() {
count++;
console.log(count);
},
decrement: function() {
count--;
console.log(count);
},
getCount: function() {
return count;
}
};
}
const myCounter = createCounter();
myCounter.increment(); // 1
myCounter.increment(); // 2
myCounter.decrement(); // 1
console.log(myCounter.getCount()); // 1
// ما تقدر توصل لـ count مباشرة
// console.log(myCounter.count); // undefined
لاحظ هنا إن
countما تقدر توصل له مباشرة منmyCounter. بس تقدر تتعامل معاه عن طريق الدوالincrementوdecrementوgetCount.
2. باستخدام الـ Classes (ES6 وما بعدها)
مع الـ Classes في ES6، صار فيه طرق أوضح للتغليف. كان فيه اتفاقية (convention) إنك تحط _ قبل اسم المتغير عشان تقول "هذا خاص"، لكنها مو إلزامية، يعني أي أحد يقدر يوصل له.
class BankAccount {
constructor(initialBalance) {
this._balance = initialBalance; // اتفاقية: هذا خاص، لكن ممكن الوصول إليه من الخارج
}
deposit(amount) {
if (amount > 0) {
this._balance += amount;
console.log(Deposited: ${amount}. New balance: ${this._balance});
}
}
withdraw(amount) {
if (amount > 0 && this._balance >= amount) {
this._balance -= amount;
console.log(Withdrew: ${amount}. New balance: ${this._balance});
} else {
console.log("Insufficient funds or invalid amount.");
}
}
getBalance() {
return this._balance;
}
}
const account = new BankAccount(100);
account.deposit(50); // Deposited: 50. New balance: 150
account.withdraw(30); // Withdrew: 30. New balance: 120
console.log(account.getBalance()); // 120
// تقدر توصل لـ _balance مباشرة (لكن المفترض ما تسوي كذا)
account._balance = -1000; // هذا يكسر التغليف!
console.log(account.getBalance()); // -1000
لكن مع الإصدارات الأحدث من جافاسكريبت، صار فيه Private Class Fields باستخدام علامة #، وهذي تخلي المتغيرات خاصة بجد ما تقدر توصلها من برا الكلاس.
class SecureBankAccount {
#balance; // هذا متغير خاص (private field)
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
console.log(Deposited: ${amount}. New balance: ${this.#balance});
}
}
withdraw(amount) {
if (amount > 0 && this.#balance >= amount) {
this.#balance -= amount;
console.log(Withdrew: ${amount}. New balance: ${this.#balance});
} else {
console.log("Insufficient funds or invalid amount.");
}
}
getBalance() {
return this.#balance;
}
}
const secureAccount = new SecureBankAccount(200);
secureAccount.deposit(70); // Deposited: 70. New balance: 270
// secureAccount.#balance = -500; // هذا بيعطيك خطأ: Private field '#balance' must be declared in an enclosing class
console.log(secureAccount.getBalance()); // 270
هذي الطريقة
#privateFieldهي الأفضل حالياً لتحقيق التغليف الحقيقي في الكلاسات، لأنها تمنع الوصول المباشر للمتغيرات الخاصة.
3. باستخدام الـ Modules (ES6)
الـ Modules في جافاسكريبت توفر لك تغليف طبيعي. أي شيء تعرفه داخل الموديول وما تسوي له export، يعتبر خاص بالموديول وما تقدر توصل له من برا.
مثال: ملف mathUtils.js
// mathUtils.js
const PI = 3.14159; // خاص بهذا الموديول
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
export { add, subtract }; // بس الدوال هذي اللي نقدر نوصلها من برا
مثال: ملف app.js
// app.js
import { add, subtract } from './mathUtils.js';
console.log(add(5, 3)); // 8
console.log(subtract(10, 4)); // 6
// console.log(PI); // هذا بيعطيك خطأ: PI is not defined
هنا
PIيعتبر خاص بملفmathUtils.jsوما تقدر تستخدمه فيapp.jsإلا لو سويت لهexport.
خلاصة الكلام
التغليف مهم جداً عشان تبني كود قوي، منظم، وسهل الصيانة. في جافاسكريبت عندك خيارات متعددة لتحقيقه، من الـ Closures القديمة، مروراً بالـ Classes واتفاقيات التسمية، وصولاً للـ Private Class Fields و الـ Modules اللي توفر تغليف حقيقي وفعال. اختار الطريقة اللي تناسب مشروعك وتخليه أنظف وأكثر أماناً.