Programming Basics SQL HTML CSS JavaScript Python C++ Java JavaFX Swing Problem Solving English English Conversations Computer Fundamentals Learn Typing

جافاسكربتالتغليف

  • مفهوم التغليف
  • أهمية التغليف
  • طرق تطبيق التغليف
  • دوال Setter و Getter
  • الكلمات المفتاحية get و set
  • الإستفادة العملية من التغليف

مفهوم التغليف

التغليف ( Encapsulation ) عبارة عن أسلوب يمكن اتباعه لإخفاء خصائص الكلاس ( Class Attributes ) بهدف حمايتها من أي تعديلات خارجية، فيكون التعامل معها ممكن فقط من خلال دوال أخرى موجودة في الكلاس.

في هذا الدرس ستتعرف على أهمية التغليف و جميع الطرق التي يمكنك اتباعها لتطبيق هذا الأسلوب.

أهمية التغليف

حتى تتضح لك أهمية التغليف، سنسلط الضوء على المشاكل التي قد تحدث في حال لم يتم تغليف خصائص الكلاس.

في المثال التالي، قمنا بتعريف كلاس إسمه Person يحتوي على الخصائص التالية:

  • name يفترض أن تحتوي نص يمثل الإسم.
  • job يفترض أن تحتوي على نصل يمثل إسم المهنة.
  • age يفترض أن تحتوي على رقم يمثل العمر.

بعدها قمنا بإنشاء كائن من هذا الكلاس مع إعطاء قيم لجميع خصائصه و بدون مراعاة أنواع القيم التي يجب وضعها فيها.

مثال

// Person هنا قمنا بتعريف كلاس إسمه
class Person {
name;
job;
age;
}
// p إسمه Person هنا قمنا بإنشاء كائن من الكلاس
p = new Person();
// p هنا قمنا بإعطاء قيم لجميع خصائص الكائن
p.name = true;
p.job = false;
p.age = '20';
// p هنا قمنا بطباعة جميع قيم خصائص الكائن
document.write('Name: ' + p.name + '<br>');
document.write('Job: ' + p.job + '<br>');
document.write('Age: ' + p.age + '<br>');
// Person هنا قمنا بتعريف كلاس إسمه class Person { name; job; age; } // p إسمه Person هنا قمنا بإنشاء كائن من الكلاس p = new Person(); // p هنا قمنا بإعطاء قيم لجميع خصائص الكائن p.name = true; p.job = false; p.age = '20'; // p هنا قمنا بطباعة جميع قيم خصائص الكائن document.write('Name: ' + p.name + '<br>'); document.write('Job: ' + p.job + '<br>'); document.write('Age: ' + p.age + '<br>');
جرب الكود

لاحظ أنه إذا لم يتم تغليف الخصائص فإنه يمكن وضع قيم من أي نوع كان فيها. هذا الأمر قد يسبب مشاكل كبيرة إذا كان الكلاس يحتوي على دوال مبنية على قيم هذه الخصائص لأنه لا يمكن توقع نوع و حدود القيم الموجودة فيها.

طرق تطبيق التغليف

إفتراضياً، الخصائص التي يتم وضعها في الكلاس تكون عامة ( Public ) مما يعني أنه بإمكان أي كائن يتم إنشاؤه من الكلاس أن يصل إليها بشكل مباشر سواء كان الهدف الحصول على قيمتها أو تحديثها.

في حال أردت إخفاء خصائص كلاس بحيث لا يكون للكائن الذي يتم إنشاؤه منه القدرة على الوصول بشكل مباشر لها فإنه يجب جعلها خاصة ( Private ) و هذا الأمر يتم من خلال وضع الرمز # قبل أسمائها فقط.

من بعدها، يجب أن توفير دوال عامة في الكلاس نفسه يمكن من خلالها الحصول على قيم الخصائص و دوال يمكن من خلالها تعيين أو تحديث قيمها.

إبتداءاً من الإصدار ES12 أصبح بالإمكان استخدام الرمز # لإخفاء خصائص الكلاس. في حال استخدام هذا الرمز في متصفح لا يدعم هذا الإصدار بعد فإنه سيتم اعتباره كحرف عادي، أي لن لا يسبب أي مشكلة و لكن الخصائص حينها ستظل بمثابة خصائص عامة.

دوال Setter و Getter

الدوال التي يتم تجهيزها للتعامل مع خصائص الكلاس المخفية يتم تسميتها على النحو التالي:

  • إذا كانت ستستعمل للحصول على قيم الخصائص فإنه يتم جعلها تبدأ بالكلمة get و يليها إسم الخاصية، مثل الدالة getName().
  • إذا كانت ستستعمل لتعيين قيم للخصائص فإنه يتم جعلها تبدأ بالكلمة set و يليها إسم الخاصية، مثل الدالة setName().

في المثال التالي قمنا بجعل خصائص الكلاس مخفية و أضفنا دوال يمكن من خلالها التعامل معها.

مثال

// Person هنا قمنا بتعريف كلاس إسمه
class Person {
// هنا قمنا بجعل خصائص الكلاس مخفية
#name;
#job;
#age;
// #name هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
getName() {
return this.#name;
}
// #name هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
setName(name) {
this.#name = name;
}
// #job هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
getJob() {
return this.#job;
}
// #job هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
setJob(job) {
this.#job = job;
}
// #age هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
getAge() {
return this.#age;
}
// #age هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
setAge(age) {
this.#age = age;
}
}
// p إسمه Person هنا قمنا بإنشاء كائن من الكلاس
p = new Person();
// p هنا قمنا بإعطاء قيم لجميع خصائص الكائن
p.setName('Mhamad');
p.setJob('Full stack developer');
p.setAge(29);
// p هنا قمنا بطباعة جميع قيم خصائص الكائن
document.write('Name: ' + p.getName() + '<br>');
document.write('Job: ' + p.getJob() + '<br>');
document.write('Age: ' + p.getAge() + '<br>');
// Person هنا قمنا بتعريف كلاس إسمه class Person { // هنا قمنا بجعل خصائص الكلاس مخفية #name; #job; #age; // #name هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية getName() { return this.#name; } // #name هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية setName(name) { this.#name = name; } // #job هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية getJob() { return this.#job; } // #job هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية setJob(job) { this.#job = job; } // #age هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية getAge() { return this.#age; } // #age هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية setAge(age) { this.#age = age; } } // p إسمه Person هنا قمنا بإنشاء كائن من الكلاس p = new Person(); // p هنا قمنا بإعطاء قيم لجميع خصائص الكائن p.setName('Mhamad'); p.setJob('Full stack developer'); p.setAge(29); // p هنا قمنا بطباعة جميع قيم خصائص الكائن document.write('Name: ' + p.getName() + '<br>'); document.write('Job: ' + p.getJob() + '<br>'); document.write('Age: ' + p.getAge() + '<br>');
جرب الكود

الدوال العامة التي يتم وضعها في الكلاس بهدف الوصول لخصائصه يمكن من خلالها أن يتم فحص القيم التي سيتم تخزينها في الخصائص و يمكن أيضاً تحديد كيف سيتم إرجاع القيم.

الكلمات المفتاحية get و set

إبتداءاً من الإصدار ES6 أصبح بالإمكان استخدام الكلمات المفتاحية get و set لجعل أسماء الدوال التي يمكن من خلالها التعامل مع الخصائص هي نفسها أسماء الخصائص.

هنا عندما يرى مترجم جافاسكربت أنك مررت قيمة للدالة فإنه سيعلم أنك تريد استدعاء الدالة التي تقوم بتعيين قيمة للخاصية، أما إذا وجدك قمت باستدعاء الدالة و لم تمرر لها قيمة فإنه سيفهم أنك ترد استدعاء الدالة التي ترجع قيمة الخاصية.

في المثال التالي قمنا بجعل خصائص الكلاس مخفية و أضفنا دوال يمكن من خلالها التعامل معها.

مثال

// Person هنا قمنا بتعريف كلاس إسمه
class Person {
// هنا قمنا بجعل خصائص الكلاس مخفية
#name;
#job;
#age;
// #name هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
get name() {
return this.#name;
}
// #name هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
set name(name) {
this.#name = name;
}
// #job هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
get job() {
return this.#job;
}
// #job هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
set job(job) {
this.#job = job;
}
// #age هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
get age() {
return this.#age;
}
// #age هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
set age(age) {
this.#age = age;
}
}
// p إسمه Person هنا قمنا بإنشاء كائن من الكلاس
p = new Person();
// p هنا قمنا بإعطاء قيم لجميع خصائص الكائن
p.name = 'Mhamad';
p.job = 'Full stack developer';
p.age = 29;
// p هنا قمنا بطباعة جميع قيم خصائص الكائن
document.write('Name: ' + p.name + '<br>');
document.write('Job: ' + p.job + '<br>');
document.write('Age: ' + p.age + '<br>');
// Person هنا قمنا بتعريف كلاس إسمه class Person { // هنا قمنا بجعل خصائص الكلاس مخفية #name; #job; #age; // #name هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية get name() { return this.#name; } // #name هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية set name(name) { this.#name = name; } // #job هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية get job() { return this.#job; } // #job هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية set job(job) { this.#job = job; } // #age هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية get age() { return this.#age; } // #age هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية set age(age) { this.#age = age; } } // p إسمه Person هنا قمنا بإنشاء كائن من الكلاس p = new Person(); // p هنا قمنا بإعطاء قيم لجميع خصائص الكائن p.name = 'Mhamad'; p.job = 'Full stack developer'; p.age = 29; // p هنا قمنا بطباعة جميع قيم خصائص الكائن document.write('Name: ' + p.name + '<br>'); document.write('Job: ' + p.job + '<br>'); document.write('Age: ' + p.age + '<br>');
جرب الكود

في المثال السابق قد يبدو لك أننا نتعامل مع خصائص الكائن المخفية بشكل مباشر و لكننا فعلياً نتعامل مع الدوال get و set التي بدورها تصلنا بهم و الدليل أننا في الكائن p لم نكتب الرمز # قبل أسماء الخصائص.

الإستفادة العملية من التغليف

بدايةً، التغليف هو أسلوب ممتاز و متعارف عليه في ترتيب و تنظيم الكود و هذا الأمر يساعد على استخدامه و صيانته و مشاركته مع الغير.

الآن لو أردت معرفة كيف يمكن من خلال التغليف أن يتم فحص القيم قبل تخزينها و كيف يمكن أيضاُ أن يتم جلبها بالطريقة التي نريدها عند طلبها فهذا الأمر يتم من خلال تعديل الدوال العامة التي يمكن من خلالها الوصول للخصائص.

في المثال التالي جعلنا الخاصية name تقبل الإسم فقط إذا كان عبارة نص و يتكون من 3 أحرف على الأقل، و عندما نريد الحصول على قيمته فإنها تقوم بشكل تلقائي بإرجاع نسخة من قيمته جميع أحرفها كبيرة.

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

مثال

// Person هنا قمنا بتعريف كلاس إسمه
class Person {
// هنا قمنا بجعل خصائص الكلاس مخفية
#name;
#job;
#age;
// #name هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
get name() {
// هنا جعلنا أحرف الإسم يتم إرجاعها على شكل أحرف كبيرة
return this.#name.toUpperCase();
}
// #name هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
set name(name) {
// أولاً قمنا سيتم فحص الإسم الذي يتم تمريره لها لمعرفة ما إن كان نص
if (typeof name !== 'string') {
throw 'Name should be a string';
}
// ثانياً سيتم فحص الإسم لمعرفة ما إن كان يتكون من 3 أحرف على الأقل
if (name.length < 3) {
throw 'Name should contain at least 3 letters';
}
// #name إذا تحققت كل الشروط السابقة سيتم تخزينه في الخاصية
this.#name = name;
}
// #job هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
get job() {
return this.#job;
}
// #job هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
set job(job) {
this.#job = job;
}
// #age هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية
get age() {
return this.#age.to;
}
// #age هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية
set age(age) {
this.#age = age;
}
}
// p إسمه Person هنا قمنا بإنشاء كائن من الكلاس
p = new Person();
// p هنا قمنا بإعطاء قيم لجميع خصائص الكائن
p.name = 'Mhamad';
p.job = 'Full stack developer';
p.age = 29;
// p هنا قمنا بطباعة جميع قيم خصائص الكائن
document.write('Name: ' + p.name + '<br>');
document.write('Job: ' + p.job + '<br>');
document.write('Age: ' + p.age + '<br>');
// Person هنا قمنا بتعريف كلاس إسمه class Person { // هنا قمنا بجعل خصائص الكلاس مخفية #name; #job; #age; // #name هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية get name() { // هنا جعلنا أحرف الإسم يتم إرجاعها على شكل أحرف كبيرة return this.#name.toUpperCase(); } // #name هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية set name(name) { // أولاً قمنا سيتم فحص الإسم الذي يتم تمريره لها لمعرفة ما إن كان نص if (typeof name !== 'string') { throw 'Name should be a string'; } // ثانياً سيتم فحص الإسم لمعرفة ما إن كان يتكون من 3 أحرف على الأقل if (name.length < 3) { throw 'Name should contain at least 3 letters'; } // #name إذا تحققت كل الشروط السابقة سيتم تخزينه في الخاصية this.#name = name; } // #job هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية get job() { return this.#job; } // #job هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية set job(job) { this.#job = job; } // #age هنا قمنا بتجهيز دالة يمكن من خلالها الحصول على قيمة الخاصية get age() { return this.#age.to; } // #age هنا قمنا بتجهيز دالة يمكن من خلالها تعيين قيمة الخاصية set age(age) { this.#age = age; } } // p إسمه Person هنا قمنا بإنشاء كائن من الكلاس p = new Person(); // p هنا قمنا بإعطاء قيم لجميع خصائص الكائن p.name = 'Mhamad'; p.job = 'Full stack developer'; p.age = 29; // p هنا قمنا بطباعة جميع قيم خصائص الكائن document.write('Name: ' + p.name + '<br>'); document.write('Job: ' + p.job + '<br>'); document.write('Age: ' + p.age + '<br>');
جرب الكود

الأمر throw يظهر الأخطاء في الكونسول و ليس في الصفحة.
إذا أردت أن يتم إظهار الخطأ في الصفحة يمكنك استخدام الدالة alert() بدلاً منه.