اساسيات الكائن في جافاسكريبت

في المادة الأولى من هذا الدرس سنتطرق إلى كائنات الجافا سكريبت، ثم سنلقي نظرة على التركيبة الأساسية لكائن الجافاسكريبت، ومن ثم إعادة النظر في بعض الميزات التي سبق وأن درسناها في وقت سابق من هذه الدورة التدريبية. علينا التأكيد أن العديد من الميزات التي قمت بالفعل بالتعامل معها هي في الحقيقة كائنات.

المتطلبات الأساسية:   دراية لا بأس بها بخصوص الحاسوب، الإلمام بمبادئ وأساسيات HTML و CSS، وبطبيعة الحال التمكن من أساسيات الجافاسكريبت (شاهد First steps وأيضا Building blocks).  
الهدف : التمكن من فهم لغة البرمجة كائنية التوجه وكيف أن  (" معظم الأشياء كائنات ") في الجافا سكريبت
وأيضا الشروع في العمل مع كائنات الجافا سكريبت.

أساسيات الكائن

أولا وقبل كل شيء، قم بتحميل نسخة من الملف التالي oojs.html. هذا الملف يتضمن العنصر —  <script> سنستخدمه في كتابة الكود الخاص بنا، وعنصر الإدخال <input> سنستخدمه لإدخال بعض التعليمات البرمجية،  وبعض المتغيرات والوظائف لاستخراج النتائج من حقل المدخلات ووضعها في العنصر <p>. سنستخدم هذه  العناصر كأساس، لدراسة أساسيات بناء الكائن.

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

var person = {};

قم بإدخال person  إلى حقل المدخلات الخاص بك واضغط على الزر button، لتحصل على النتيجة التالية:

[object Object]

تهانينا، لقد أنشأت للتو  أول كائن خاص بك. أنجزت المهمة! لكن هذا كائن فارغ، دعونا نقوم بتحديث هذ الكائن ليصبح على هذا الشكل:

var person = {
  name : ['Bob', 'Smith'],
  age : 32,
  gender : 'male',
  interests : ['music', 'skiing'],
  bio : function() {
    alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
  },
  greeting: function() {
    alert('Hi! I\'m ' + this.name[0] + '.');
  }
};

بعد الحفظ والتحديث، قم بإدخال التعليمات البرمجية التالية واحدة تلو الاخرى في حقل المدخلات الخاص بك:

person.name[0]
person.age
person.interests[1]
person.bio()
person.greeting()

لقد حصلت الآن على بعض البيانات والوظائف من داخل الكائن الخاص بك، جميل! أصبحت الآن قادرا على الوصول إلى البعض من التركيبة الاساسية للكائن.

ملاحظة : إذا واجهتك صعوبة في الحصول على هذا العمل، حاول مقارنة التعليمات البرمجية الخاصة بك مع النسخة لدينا، انظر oojs-finished.html (يمكنك ايضا see it running live). من الأخطاء الشائعة أثناء العمل مع الكائنات هو وضع فاصلة في نهاية آخر العضو — وهذا سوف يسبب خطأ.

هذا ما يحدث هنا؟ لدينا كائن يتكون من عدة أعضاء، لكل واحد منهم اسماً (مثل name و age أعلاه)، وقيمة ( مثل ['Bob', 'Smith'] و 32). كل زوج (name: value) يجب أن يفصلا بفاصلة، والاسم والقيمة في كل حالة مفصولة بفاصلة منقوطة. فيما تكون دائماً، تركيبة الكائن الأساسية على هذا النحو :

var objectName = {
  member1Name : member1Value,
  member2Name : member2Value,
  member3Name : member3Value
}

قيمة العضو  في الكائن يمكن أن تكون أي شيء، في الكائن person لدينا سلسلة نصية، وقيمة رقمية، ومصفوفتان، ووظيفتان. العناصر الأربعة الأولى تسمى عناصر البيانات، ويشار إليها بخصائص الكائن (object properties). والعنصرين الأخيرين هما من الوظائف التي تسمح للكائن أن يفعل شيئا مع تلك البيانات، ويشار إليها بوظائف الكائن (object methods).

يسمى هذا النوع من الكائنات بـ object literal — لقد كتبنا حرفيا محتويات الكائن إلى أن وصلنا إلى إنشائه. هذا على النقيض من الكائنات المبنية على الأصناف (classes)، والتي سوف نتطرق إليها في وقت لاحق.

من الشائع جدا إنشاء كائن باستخدام  object literal لنقل سلسلة من البيانات الهيكلية، البيانات المتصلة بطريقة ما، على سبيل المثال إرسال طلب إلى الخادم (server) لوضعها في قاعدة البيانات. إرسال كائن واحد سيكون أكثر كفاءة بكثير من إرسال عدة عناصر على حدة، وأسهل مقارنة بالعمل مع المصفوفة (Array)، حينما تريد تحديد عناصر فردية حسب الاسم.

رمز النقطة

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

person.age
person.interests[1]
person.bio()

Sub-namespaces

من الممكن تغيير شكل التعليمة البرمجية، من مصفوفة الى Sub-namespaces. على سبيل المثال، حاول تغيير اسم العضو من :

name : ['Bob', 'Smith'],

الى

name : {
  first : 'Bob',
  last : 'Smith'
},

على نحو فعال قمنا بانشاء Sub-namespaces. هذا يبدو معقدا، في الحقيقة هو ليس كذالك - للوصول إلى هذه العناصر كل ما تحتاج اليه هو  ربط سلسلة العناصر بنقطة الترميز. جرب هذا :

person.name.first
person.name.last

هام : في هذه الحالة ستحتاج أيضا للذهاب الى الكود الخاص بك واستبدال الحالات التالية من :

name[0]
name[1]

الى

name.first
name.last

وإلا، فسوف لن تعمل الوظائف الخاصة بك.

رمز الاقواس

هناك طريقة أخرى للوصول إلى خصائص الكائن – باستخدام رمز الاقواس. بدلاً من استخدام هذه :

person.age
person.name.first

يمكنك استخدام هذه

person['age']
person['name']['first']

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

إعداد اعضاء الكائن

حتى الآن قمنا فقط بارجاع/ جلب اعضاء الكائن - يمكننا أيضا تعيين/تحديث  اعضاء الكائن ببساطة عن طريق الإعلان عن العضو الذي نريد تحديثه سواء باستخدام النقطة أو برمز الاقواس، كالتالي :

person.age = 45
person['name']['last'] = 'Cratchit'

في حقل المدخلات الخاص بك جرب ادخال التعليمات البرمجية التالية واحدة تلوى الاخرى للحصول على الاعضاء مرة أخرى بعد التحديث :

person.age
person['name']['last']

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

person['eyes'] = 'hazel'
person.farewell = function() { alert("Bye everybody!") }

يمكنك الآن اختبار ألاعضاء الجدد خاصتك :

person['eyes']
person.farewell()

احد اهم الجوانب المفيدة في استخدام رموز الاقواس هو امكانية استخدامها ليس فقط لتحديد قيم الاعضاء، ولكن أسماء الأعضاء أيضا. دعونا نقول أننا نريد أن يتمكن المستخدم من تخزين قيمة مخصصة لبيانات الاشخاص، عن طريق كتابة اسم العضو وقيمته في اثنين من المدخلات النصية؟ يمكن أن نحصل على تلك القيم كالتالي :

var myDataName = nameInput.value
var myDataValue = nameValue.value

يمكننا بعد ذلك إضافة اسم العضو الجديد وقيمته إلى الكائن person  كالتالي :

person[myDataName] = myDataValue

لاختبار هذا، قم بإضافة الأسطر التالية أسفل التعليمات البرمجية خاصتك،  بعد قوس الاغلاق للكائن person :

var myDataName = 'height'
var myDataValue = '1.75m'
person[myDataName] = myDataValue

الآن قم بحفظ وتحديث، وادخل ما يلي في حقل المدخلات الخاص بك :

person.height

وهذا لم يكن ممكناً مع طريقة نقطة الترميز، لانها تقبل اسم العضو كقيمة حرفية فقط، والتالي لا يمكن لهذه القيمة الحرفية ان تشير إلى اسم المتغير.

ما هي this؟       

لربما لاحظت شيئا غريبا بعض الشيء في الوظائف الخاصة بنا؟. القي نظرة على هذه  على سبيل المثال:

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


ربما تسألت ما هي this. هي كلمة من الكلمات المحجوزة للغة جافاسكريبت.
 تشير إلى الكائن الحالي الذي تم كتابة التعليمات البرمجية داخله — في هذه الحالة هي تعادل person. فلماذا لا نستخدم person بدلا من ذلك؟
كما سترى في  جافاسكريبت - البرمجة غرضية التوجه للمبتدئين عندما نبدأ بانشاء constructors، ... الخ، this مفيدة جداً — لانها ستضمن دائما أن تستخدم القيم الصحيحة عندما يتغير سياق العضو.
( على سبيل المثال عند وجود حالتين مختلفتين للكائن person بأسماء مختلفة، سوف ترغب في التعامل مع ال name الموجودة في كل منهما عن طريق الوظيفة ()greeting ).

ولتوضيح ما نعنيه مع زوج من الكائنات person1  و person2 :

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

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

في هذه الحالة، الوظيفة ()greeting  الخاصة بالكائن person1، ستظهر ".Hi! I'm Chris".
 فيما الوظيفة ()greeting الخاصة بالكائن person2، ستقوم باظهار ".Hi! I'm Brian".
على الرغم من ان التعليمات البرمجية للوظيفة greeting هي نفسها في كلا الحالتين. وكما قلنا في وقت سابق، this تساوي الكائن داخل التعليمات البرمجية — هي ليست مفيدة بشكل كبير خصوصا عند الكتابة خارج ال object literals، لكنها مفيدة جدا عندما تريد توليد كائن حيوي (على سبيل المثال باستخدام ال constructors).  سوف تتضح لك الامور في وقت لاحق.

استخدمنا الكائنات طوال المرحلة

من خلال الأمثلة السابقة، لربما فكرت بأن نقطة الترميز التي استخدمناها مؤلوفة جدا. هذا لأننا استخدمناها تقريبا في جميع المراحل السابقة، في كل مرة كنا نعمل من خلال مثال يستخدم اما built-in browser API او JavaScript object، استخدمنا الكائنات، نظراً لأن هذه الميزات تم بناؤها باستخدام نفس النوع من ال object structures مثل ما راينا هنا،
وإن كانت أكثر تعقيدا من الأمثلة الخاصة بنا.

عند استخدامك لوظائف السلاسل النصية string methods، كهذه :

myString.split(',');

فقد استخدمت وظيفة متاحة في مثيل الفئة  String. في كل مرة تقوم فيها بإنشاء سلسلة نصية في التعليمات البرمجية الخاصة بك، بشكل اوتوماتيكي يتم انشاء هذه السلسلة النصية ك instance  (مثيل) من String، وبالتالي سيصبح متاح لها العديد من ال methods/properties الخاصة بال String.


عندما تصل  الى document object model باستخدام سطور كهذه :

var myDiv = document.createElement('div');
var myVideo = document.querySelector('video');

فقد استخدمت وظيفة متاحة في مثيل الفئة Document.  في كل مرة تحمل فيها صفحة الويب، يتم إنشاء instance (مثيل) من الوثيقة، تسمى document، والتي تمثل بنية الصفحة كاملة، والمحتوى، وغيرها من الميزات مثل عنوان URL الخاص بها. وهذا يعني أن لديها العديد من ال methods/properties المتاحة لها.

وينطبق الأمر نفسه على اي من (كائنات برمجة واجهة التطبيقات المدمجة built-in object/API)  الاخرى قد تمت باستخدام  —  Array, Math, etc.

لاحظ ان الكائنات المدمجة Objects/APIs لا تقوم دائما بانشاء object instances (مثيلات الكائن) اوتوماتيكيا. وكمثال على ذلك، Notifications API — التي تسمح للمتصفحات الحديثة باطلاق system notifications - يتطلب منك إنشاء مثيل object instance جديد باستخدام ال constructor لكل اشعار تريد اطلاقه.

قم بادخال التعليمة التالية في console الجافاسكريبت الخاص بك :

var myNotification = new Notification('Hello!');

سوف نبحث في ال constructor في مقال لاحق.

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

ملخص

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

في المقال القادم سنبدأ بالنظر في البرمجة كائنية التوجه (OOP) ، وكيف يمكن استخدام هذه التقنية في جافا سكريبت.

Document Tags and Contributors

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