MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

جافاسكريبت - البرمجة غرضية التوجه للمبتدئين

مع ما تعلمناه من أساسيات، سنركز الآن على جافاسكريبت غرضية التوجه (object-oriented JavaScript (OOJS.
سنستعرض في هذه المقالة أساسيات البرمجة غرضية التوجه (object-oriented programming (OOP نظريا،
ثم سنستكشف كيف أن الجافا سكريبت تظاهي الـ object classes المستخدمة في لغات البرمجة الأخرى (php مثلا) عن طريق الـ constructor functions، وأيضا كيفية إنشاء instances من الكائن (أو مثيل الكائن).

المتطلبات الأساسية :

دراية لا باس بها بخصوص الحاسوب، الإلمام بمبادئ وأساسيات الـ HTML وCSS وjavascript (شاهد First steps و Building blocks)  وأساسيات بناء الـكائنات OOJS. (شاهد Introduction to objects).

الهدف : فهم نظريا الـبرمجة غرضية التوجه (object-oriented programming)، وكيف أن كل شيء في الجافاسكريبت عبارة عن كائن (object) !.
وأيضا كيفية إنشاء الـ constructors وobject instances.

البرمجة الكائنية (أو البرمجة غرضية التوجه) 

لنبدأ، دعونا نقوم بعرض نبذة بسيطة عن ماهي البرمجة غرضية التوجه ( Object-oriented programming ( OOP على مهل. نقول على مهل، لأن فهم واستيعاب البرمجة غرضية التوجه (OOP) على عجل سيكون امر غاية في التعقيد، كأن نعطيك العلاج الكامل الآن على الأرجح سيسبب لك خلطا أكثر مما سيساعدك.   
الفكرة الأساسية من البرمجة غرضية التوجه OOP هي أن نستخدم أشياءً من عالمنا الحقيقي ونحولها إلى كائنات ونقوم بتمثيلها داخل برامجنا، ونوفر وسائل سهلة للوصول إلى وظائفها، حتى نستفيد منها.

يتكون الكائن من مجموعة من البيانات والتعليمات البرمجية المتصلة فيما بينها. والتي تمثل معلومات عن الشيء الذي نتعامل معه، والوظيفة أو السلوك الذي نريد منه أن يقوم به. بيانات الكائن في كثير من الأحيان تكون عبارة عن وظائف كثيرة يمكن تخزينها بدقة (أو تغليفها) داخل حزمة الكائن (object package). حزمة الكائن يمكن أن تمتلك اسما يشير إليها، وهو ما يسمى في بعض الأحيان ب namespace. ما يجعل من السهل إعادة هيكلتها أو الوصول إليها، كما يمكن أيضا استخدام الكائنات كمخازن للبيانات حتى يمكن إرسالها بسهولة عبر شبكة الاتصال.
 

تعريف الـ object template

دعونا ننظر في برنامج سهل يستعرض معلومات عن الطلاب والمعلمين في المدرسة.
هنا سوف نبحث في نظرية البرمجة الغرضية التوجه OOP بشكل عام، وليس في سياق أي لغة برمجة معينة.

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

في بعض لغات البرمجة غرضية التوجه OOP الأخرى (PHP مثلا)، يعرف الكائن عامة بال  class (جافا سكريبت تستخدم مصطلحات وآلية مختلفة، كما سنرى أدناه) - في الواقع هو ليس كائنا، بل هو القالب الذي يعرف خصائص الكائن وكيف ينبغي له أن يكون.

إنشاء كائنات فعلية - Creating actual objects 

من خلال الـ class، يمكنك إنشاء object instances  — وهي الكائنات التي تحتوي على البيانات والوظائف المحددة في الـ class (وبالتالي هي مثيلة له). من خلال الـ class Person الخاص بنا، يمكننا الآن إنشاء بعض الأشخاص الفعليين :

عندما يتم إنشاء object instance من الـ class، ستبدأ الـ constructor function الخاصة بال class بإنشائه،  تسمى عملية إنشاء الـ object instance من الـ class بال instantiation (إنشاء مثيل).
أي أنَّ الـ object instance هو instantiated (مثيل) من الـ class.

كلاسات متخصصة - Specialist classes

في هذه الحالة لسنا بحاجة للأشخاص بصفة عامة، سنستخدم الأساتذة والطلاب بصفة خاصة. في البرمجة غرضية التوجه OOP، يمكنك إنشاء كلاسات بناءً على كلاسات أخرى. مثلا كلاسات الأبناء child الجديدة يمكنها أن ترث البيانات والتعليمات البرمجية من الكلاس الأب parent.
يمكنك إعادة استخدام وظيفة مشتركة بين جميع أنواع الكائنات بدلاً من الاضطرار إلى تكرار العملية عدة مرات، حيث يختلف الأداء الوظيفي من كلاس لآخر، يمكنك تخصيص ميزات معينة لكل منها حسب الحاجة.

وهذا مفيد حقاً – حيث يتشارك المعلمين والطلاب في الكثير من الخصوصيات المشتركة مثل الاسم ونوع الجنس، والعمر، سيكون من المفيد جدا تعريف تلك الخصوصيات المشتركة مرة واحدة فقط. يمكنك أيضا تعريف نفس تلك الخصوصيات كل على حدة في الكلاسات المختلفة، كما سيتم تعريف كل من هذه الخصوصيات في namespace مختلفة. على سبيل المثال، قد تكون تحية الطالب على هذا الشكل ( Yo, I'm [firstName]" ( Hi, I'm Sam"، بينما قد يستخدم المعلم شيئا أكثر رسمية،
مثل ( Hello, my name is [Prefix] [lastName]" ( Hello, My name is Mr Griffiths".

ملاحظة : قدرة كائنات متعددة على تنفيذ نفس الوظائف تسمى polymorphism (تعدد الأشكال).

يمكنك الآن إنشاء object instances من child classes. على سبيل المثال :

في باقي المقالة سنبدأ بإلقاء نظرة عملية على البرمجة غرضية التوجه OOP في جافا سكريبت.

الـ Constructors والـ object instances

بعض الناس يقولون أن جافاسكريبت ليست لغة كائنية حقيقية، لأنها لا تستخدم الكلمة المحجوزة class لإنشاء الكلاسات مثل العديد من لغات البرمجة الغرضية التوجه OOP. بدلاً من ذلك، الجافاسكريبت تستخدم وظائف خاصة تسمى constructor functions لتعريف الكائنات وميزاتها. هذه الوظائف مفيدة جدا لأنك غالبا ما ستواجه حالات، حيث لا يمكنك معرفة عدد الكائنات التي ستقوم بإنشائها. وبالتالي ستوفر لك الـ constructors الوسائل اللاَّزمة لإنشاء العديد من الكائنات التي تحتاج إليها بطريقة فعالة، وربط البيانات والوظائف كما هو مطلوب.

عندما يتم إنشاء object instance جديد من الـ constructor function، لا يتم نسخ كل الوظائف الى الكائن الجديد مثل الـ «classic OO languages». بدلاً من ذلك يتم تكوين رابط وظيفي بواسطة reference chain تسمى prototype chain  (شاهد Object prototypes). حتى هذا ليس تجسيدًا حقيقـيًّا، إذا توخينا الدقة، فجافاسكريبت تستخدم آلية مختلفة لتبادل الوظائف بين الكائنات.

ملاحظة : عدم وجود الـ "classic OOP" ليس بالضرورة أمرا سيئاً، كما ذكر أعلاه، البرمجة الغرضية التوجه OOP معقدة للغاية، لكن للجافاسكريبت بعض الطرق اللطيفة للاستفادة من ميزات الـ OO  دون الحاجة للتعمق كثيرا فيها.

لنبدأ بإنشاء كلاسات عن طريق الـ constructors، وإنشاء object instances منها في جافاسكريبت.
أولاً وقبل كل شيء، نود منك عمل نسخة من الملف التالي oojs.html الذي سبق وأن عملنا عليه في الدرس السابق.

مثال سهل

دعونا نبدأ بالنظر في كيفية تعريف شخص من خلال دالة function عادية. أضف هذه الدالة أدناه إلى التعليمات البرمجية الموجودة لديك:

function createNewPerson(name) 
{
  var obj = {};
  obj.name = name;
  obj.greeting = function () {
    alert('Hi! I\'m ' + this.name + '.');
  }
  return obj;
}

يمكنك الآن إنشاء شخص Person جديد بواسطة استدعاء هذه الدالة، حاول إدخال الأسطر التالية في console الجافاسكريبت في المتصفح الخاص بك:

var salva = createNewPerson('salva');
salva.name;
salva.greeting();

يعمل بشكل جيد، ولكنه عمل متعب قليلا. إذا كنا نعرف أننا سننشئ كائنًـا، لماذا نحتاج إلى إنشاء كائن جديد فارغ ونعود إليه  (عبر return)؟ من حسن الحظ، جافاسكريبت تزودنا باختصار مفيد على شكل constructor functions، دعونا الآن ننشئ واحدا !

قم  باستبدال الدالة function  السابقة بهذه :

function Person(name) 
{
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

الـ constructor function هو نسخة الجافاسكريبت فيما يتعلق بال class.
ستلاحظ أن لديها كافة الميزات التي تتوقعها في دالة function، على الرغم من أنها لا تقوم بإرجاع أي شيء، ولا تقوم بإنشاء كائن، لحد الآن هي مجرد تعريف للـخصائص (properties) والوظائف (methods)، سترى أيضا هذه الكلمة this المستخدمة هنا، فكرتها  أنه كلما تم إنشاء أحد الـ object instances، فإن الـ object's name property سيساوي قيمة الـ name الممررة إلى الـ constructor call،  والوظيفة () greeting ستستخدم قيمة name الممررة إلى الـ constructor call أيضا.

الـ constructor call هو الذي يكون على هذا الشكل  new Object(parameters) كما سنرى بعد قليل.

ملاحظة : عادة ما يبدأ اسم الـ constructor function بحرف كبير - يتم استخدام هذه الاتفاقية حتى يسهل التعرف على الـ constructor functions في التعليمات البرمجية بسهولة.

كيف يمكننا استدعاء الـ constructor  لإنشاء بعض الكائنات؟

أضف الأسطر التالية أدناه الى اسفل التعليمات البرمجية الخاص بك:

var person1 = new Person('Bob');
var person2 = new Person('Sarah');

احفظ التعليمات البرمجية الخاصة بك واعد تحميل الصفحة، وحاول إدخال الأسطر التالية في حقل المدخلات الخاص بك (كل على حدة) :

person1.name
person1.greeting()
person2.name
person2.greeting()

جميل! سترى الآن أن لدينا كائنان جديدان في الصفحة، تم تخزين كل منهما ضمن namespace مختلفين، للوصول إلى الخصائص والوظائف الخاصة بهما، عليك أن تبدأ بالاتصال اما مع person1 أو person2، هما الان مغلفين بدقة، وبالتالي سنضمن عدم تشابك الوظائف مع بعضها، لاحظ ان لديهم نفس الخاصية name ونفس الوظيفة () greeting، الا أن كل واحد منهما سيحصل على القيمة name التي تم تعيينها له. هذا واحد من الاسباب التي تعرفنا على اهمية this، حيث انها ستشير الى الكائن الخاص بها وليس لشئ اخر.

دعونا ننظر في ال constructor calls مرة أخرى :

var person1 = new Person('Bob');
var person2 = new Person('Sarah');

في كل حالة من الحالتين، يتم استخدام الكلمة المحجوزة new لاخبار المتصفح بأننا نريد إنشاء object instance جديد، متبوعة باسم ال function  و ال parameters المطلوبة بين الأقواس، ويتم تخزين النتيجة في متغير، وهي شبيهة جدا بكيفية استدعاء   standard function، ويتم إنشاء كل instance وفقا لهذا التعريف:

function Person(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

بعد أن يتم إنشاء الكائنات الجديدة، فعليا، ستحتوي المتغيرات person1 و person2 على الكائنات التالية على هذا النحو :

{
  name : 'Bob',
  greeting : function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

{
  name : 'Sarah',
  greeting : function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

قلنا فعليا لأنه في الواقع الفعلي لا يزال يتم تعريف الوظائف في ال class، وليس في object instances، على عكس ال object literal كما راينا سابقا.

انشاء  constructor

المثال الذي راينا اعلاه، كان مجرد مقدمة، دعونا الان ننشئ Person() constructor function. فعلي.

قم بحذف التعليمات البرمجية التي قمت بإدراجها حتى الآن، واضف هذا ال constructor بدلا منه – هو بالضبط نفس المثال البسيط اعلاه من حيث المبدأ، مع القليل من التعقيد :

function Person(first, last, age, gender, interests) {
  this.name = {
    first,
    last
  };
  this.age = age;
  this.gender = gender;
  this.interests = interests;
  this.bio = function() {
    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
  };
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name.first + '.');
  };
};

الآن أضف ال constructor call التالي، لإنشاء object instance من ال constructor خاصتنا :

var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);

يمكنك الآن الوصول إلى الخصائص والوظائف مثلما فعلنا مع الكائن اعلاه :

person1['age']
person1.interests[1]
person1.bio()
// etc.

ملاحظة: إذا كنت تواجه صعوبة في الحصول على هذا العمل، حاول مقارنة التعليمات البرمجية الخاصة بك مع التعليمات الخاصة بنا — see oojs-class-finished.html (also see it running live).

امتحان بسيط

في هذا الامتحان البسيط وجب عليك إضافة بضعة أسطر اضافية الى الكائن Person خاصتك، ومحاولة جلب وإعداد الاعضاء من ناتج ال object instances.

هناك مشكلتين بخصوص الوظيفة ()bio :
أ- مخرجاتها ستكون دائما عبارة عن الضمير هو "He"، حتى وان كان الشخص انثى.
ب- الوظيفة bio سترجع اثنين من الاهتمامات (interests) فقط، حتى وان تم تعيين أكثر من ذالك في مصفوفة الاهتمامات interests.
يمكن العمل على حل هذه المشكلة داخل ال ?( class definition ( constructor.
يمكنك وضع أي كود تريده داخل ال constructor (على الأرجح سوف تحتاج لبضع جمل شرطية "if..else if...else" وحلقة "for").
مطلوب منك حل هذه المشكلة آخدا بعين الاعتبار نوع الجنس (ذكر/انثى). وايضا عدد الاهتمامات interests كان تكون 1 أو 2 أو أكثر من 2. 

ملاحظة : إذا واجهتك مشكلة، فقد قمنا بتوفير الاجابة، answer inside our GitHub repo (see it live) — ننصح بالاعتماد على نفسك اولا !.

طريقة اخرى لانشاء ال object instances

حتى الآن رأينا طريقتان مختلفتان لإنشاء ال object instance :

  1. باستخدام الاعلان عن ال object literal (الدرس السابق).
  2. باستخدام constructor function (انظر أعلاه).

هذا منطقي, ولكن هناك طرق أخرى، نريد ان نجعلها مالوفة لديك في حالة مصادفتها في مكان ما على شبكة الويب.

The Object() constructor

يمكنك استخدام ال Object() constructor لانشاء كائن جديد، نعم، حتى الكائنات العامة تحتوي على constructor والذي بدوره يولد كائنا فارغا.

حاول ادخال السطر التالي في console المتصفح الخاص بك :

var person1 = new Object();

سيقوم هذا بتخزين كائن فارغ في المتغير person1. يمكنك بعد ذالك اضافة الخصائص والوظائف لهدا الكائن،
باستخدام نقطة الترميز او باستخدام اقواس الترميز، حسب الطلب. جرب هذا المثال :

person1.name = 'Chris';
person1['age'] = 38;
person1.greeting = function() {
  alert('Hi! I\'m ' + this.name + '.');
}

يمكنك ايضا تمرير object literal الى ال Object() constructor ك parameter معبأة على شكل properties/methods. جربه بنفسك :

var person1 = new Object({
  name : 'Chris',
  age : 38,
  greeting : function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
});

باستخدام الوظيفة ()create

الجافاسكريبت لديها وظيفة مبنية مسبقا   (built-in method) تسمى ()create, والتي تسمح لك بإنشاء object instance جديد استناداً إلى كائن موجود.

جرب ادخال هذا السطر في console المتصفح الخاص بك :

var person2 = Object.create(person1);

والان جرب هذه :

person2.name
person2.greeting()

سترى أن person2 قد تم إنشاؤه استناداً إلى person1 — فاصبح يمتلك نفس الخصائص والوظائف المتاحة لل person1 وهذا مفيد جداً، حيث أنه سيسمح لك بإنشاء object instances جديد، دون الحاجة إلى تعريف ال constructor.
الجانب السلبي هو أن ال ()create غير مدعومة من قبل بعض المتصفحات، فيما يتعلق بالعودة ك constructors ( IE8 وما قبله لا يدعم هذه الوظيفة ) ، بالإضافة إلى ذلك يعتقد البعض أن ال constructors  تمنح التعليمات البرمجية الخاصة بك مميزات اكثر. يمكنك إنشاء constructors خاصة بك  في مكان واحد، ومن ثم إنشاء instances حسب الحاجة،

ومع ذلك، إذا لم تكن قلقا جداً بشأن دعم المتصفحات القديمة حقاً، وتحتاج فقط لبضع نسخ من الكائن، فان انشاء constructor قد يكون مبالغة بالنسبة للتعليمات البرمجية الخاصة بك. الأمر متروك لك ولما تفضله. ببساطة بعض الناس يجدون ال ()create. سهلة في الفهم والاستخدام.

سوف نستكشف ال ()create بمزيد من التفصيل في وقت لاحق.

ملخص

قدمت هذه المقالة طريقة مفصلة حول ال object-oriented نظريا، هذه ليست القصة بأكملها، ولكنها تعطيك فكرة عن ما نتعامل معه هنا. باﻹضافة إلى ذلك، فقد راينا الكيفية التي ترتبط بها جافا سكريبت، وكيف انها تختلف عن مفهوم ال "classic OOP"، راينا كذالك، كيفية تنفيذ ال الكلاسات classes في الجافاسكريبت باستخدام ال constructor functions، وطرق مختلفة لتوليد ال object instances.

في الدرس القادم سوف نستكشف prototypes الجافا سكريبت.

Document Tags and Contributors

 Contributors to this page: Youssef-Belmeskine, atefBB
 Last updated by: Youssef-Belmeskine,