اذهب إلى المحتوى

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


ابراهيم الخضور

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

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

ننصحك قبل أن تبدأ العمل معنا في هذه السلسلة أن تطلع على:

  1. أساسيات HTML
  2. أساسيات عمل CSS
  3. أساسيات جافا سكريبت

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

الكائن object هو مجموعة مترابطة من البيانات أو الوظائف، فهو يتألف عادة من من عدة متغيرات ودوال (تُدعى المتغيرات ضمن الكائن خاصيات properties والدوال methods). وحتى نستوعب مفهوم الكائنات سنعمل على المثال التالي. أنشئ أولًا نسخة من الملف oojs.html على حاسوبك. ويتضمن هذا الملف العنصر <script> الذي سنكتب ضمنه الشيفرة. سنستخدم هذه الصفحة كأساس لاستكشاف الصياغة اﻷساسية للكائن في جافا سكريبت. وعليك أن تفتح طرفية جافا سكريبت الخاصة بأدوات مطوري ويب وتجعلها جاهزة لكتابة التعليمات.

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

const person = {};

افتح اﻵن طرفية جافا سكريبت ثم اكتب person ضمنها ثم اضغط المفتاح Enter. من المفترض أن تحصل على نتيجة مشابهة للتالي:

[object Object]
Object { }
{ }

تهانينا! لقد أنشأت للتو كائنًا في جافا سكريبت. لقد أنجز العمل بالفعل، لكن الكائن فارغ كما تلاحظ ولا يمكن أن يفيدنا كثيرًا. لهذا سنعدّل الكائن ضمن شيفرة جافا سكريبت في ملف التمرين كالتالي:

const person = {
  name: ["Bob", "Smith"],
  age: 32,
  bio: function () {
    console.log(`${this.name[0]} ${this.name[1]} is ${this.age} years old.`);
  },
  introduceSelf: function () {
    console.log(`Hi! I'm ${this.name[0]}.`);
  },
};

جرّب إدخال بعض التعليمات كما يلي في طرفية جافا سكريبت وذلك بعد حفظ التغييرات وإعادة تحميل الصفحة:

person.name;
person.name[0];
person.age;
person.bio();
// "Bob Smith is 32 years old."
person.introduceSelf();
// "Hi! I'm Bob."

يضم الكائن بعض البيانات اﻵن كما يحتوي على بعض الوظائف التي يمكن الوصول إليها من خلال صياغة بسيطة واضحة.

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

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

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

وعندما يكون عضو الكائن دالة، باﻹمكان استخدام صياغة أبسط من bio:function، ويكفي أن نكتب ()bio كالتالي:

const person = {
  name: ["Bob", "Smith"],
  age: 32,
  bio() {
    console.log(`${this.name[0]} ${this.name[1]} is ${this.age} years old.`);
  },
  introduceSelf() {
    console.log(`Hi! I'm ${this.name[0]}.`);
  },
};

وسنستخدم من اﻵن وصاعدًا الصيغة المختصرة في تعريف الدوال.

ندعو الكائنات التي تُبنى كما شرحنا سابقًا بالكائنات الحرفية literal object لأننا كتبنا حرفيًا كل أعضائها عند إنشائها. وهي كائنات مختلفة عن تلك التي تُنسخ عن اﻷصناف، والتي سنلقي عليها نظرة لاحقًا.

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

طريقة النقطة Dot notation للوصول إلى أعضاء الكائن

لقد وصلنا في مثالنا السابق إلى خاصيات وتوابع الكائن باستخدام العامل . ويسمى هذا الأسلوبالتدوين النقطي أو الاستدعاء النقطي dot notation. حيث يسلك اسم الكائن person سلوك فضاء الأسماء namespace. ولا بد من كتاب اسم الكائن أولًا للوصول إلى أي عضو من أعضاءه ومن ثم تكتب النقطة ومن ثم العضو الذي تريد الوصول إليه، وقد يكون العضو عنصرًا أو مصفوفة أو استدعاء أحد توابع الكائن.

person.age;
person.bio();

الكائنات والخاصيات على شكل كائنات

يمكن للخاصية بحد ذاتها أن تكون كائنًا، جرّب مثلًا أن تغيّر العضو name من الشكل التالي:

const person = {
  name: ["Bob", "Smith"],
};

ليصبح بالشكل:

const person = {
  name: {
    first: "Bob",
    last: "Smith",
  },
  // …
};

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

person.name.first;
person.name.last;

فإن فعلت ذلك، عليك العودة إلى الشيفرة وتغيير كل شيفرة من الشكل:

name[0];
name[1];

إلى

name.first;
name.last;

وإلا لن تعمل التوابع.

استخدام طريقة اﻷقواس في الوصول إلى أعضاء الكائن

باﻹمكان الوصول إلى أعضاء الكائن باستخدام طريقة اﻷقواس المربعة Bracket notation فبدلًا من استخدام النقطة كما في المثال التالي:

person.age;
person.name.first;

يمكننا استخدام الأقواس كما في الكود التالي:

person["age"];
person["name"]["first"];

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

يُفضّل استخدام طريقة النقطة على طريقة اﻷقواس عمومًا لأنها أكثر وضوحًا وأسهل قراءة. لكن ستجد بعض الحالات التي تستخدم فيها اﻷقواس المربعة مثل إسناد قيمة خاصية على شكل كائن إلى متغيّر، فلن تستطيع في هذه الحالة استخدام النقطة للوصول إلى القيمة، لكن باﻹمكان استخدام الأقواس المربعة.

في المثال التالي، يمكن للدالة أن تستخدم الطريقة person[propertyName] للوصول إلى القيمة المخزنة في الخاصية proertyName:

const person = {
  name: ["Bob", "Smith"],
  age: 32,
};

function logProperty(propertyName) {
  console.log(person[propertyName]);
}

logProperty("name");
// ["Bob", "Smith"]
logProperty("age");
// 32

ضبط قيم أعضاء الكائن

كل ما تعلمناه حتى اللحظة هو الحصول على قيمة أعضاء الكائن object members، لكن باﻹمكان أيضًا ضبط قيمة هذه الأعضاء بالتصريح عن العضو الذي تريد ضبطه (باستخدام طريقة النقطة أو اﻷقواس المربعة) كالتالي:

person.age = 45;
person["name"]["last"] = "Cratchit";

جرّب إدخال التعليمتين التاليتين ضمن طرفية جافا سكريبت، ولاحظ كيف تغيرت القيمة:

person.age;
person["name"]["last"];

لا يقتصر ضبط قيم أعضاء الكائن على تغيير القيمة الموجودة للخاصيات أو التوابع، بل يمكنك إنشاء عناصر جديدة كليًا. جرّب اﻵن ما يلي في الطرفية:

person["eyes"] = "hazel";
person.farewell = function () {
  console.log("Bye everybody!");
};

اختبر اﻵن العناصر الجديدة:

person["eyes"];
person.farewell();
// "Bye everybody!"

من إيجابيات استخدام طريقة اﻷقواس المربعة أنه باﻹمكان استخدامها في ضبط قيمة اﻷعضاء ديناميكيًا إضافة إلى تغيير أسماء الأعضاء أيضًا. لنفترض أننا نريد منح المستخدم القدرة على تخزين أنواع مخصصة من القيم في بيانات اﻷشخاص وذلك بكتابة اسم العضو وقيمته ضمن مربعي إدخال نصي منفصلين. باﻹمكان إنجاز اﻷمر كالتالي:

const myDataName = nameInput.value;
const myDataValue = nameValue.value;

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

person[myDataName] = myDataValue;

ولاختبار اﻷمر، جرّب إضافة الأسطر التالية إلى شيفرتك، أسفل قوس إغلاق الكائنن person:

const myDataName = "height";
const myDataValue = "1.75m";
person[myDataName] = myDataValue;

جرّب حفظ التغييرات وإعادة تحميل الصفحة ثم إدخال التالي ضمن مربع اﻹدخال النصي:

person.height;

إن إضافة خاصية إلى الكائن باستخدام اﻷسلوب السابق لن يكون ممكنًا باستخدام طريقة النقطة والتي تقبل فقط الاسم الحرفي للعضو ولا تقبل قيمة متغير يشير إلى الاسم.

لماذا استخدمت الكلمة المحجوزة this

لربما قد لاحظت شيئًا غريبًا في أسلوبنا في الشيفرة السابقة، ألق نظرة اﻵن على هذا المثال:

introduceSelf() {
  console.log(`Hi! I'm ${this.name[0]}.`);
}

لربما تتساءل ماذا فعلت الكلمة this؟ تشير هذه الكلمة المحجوزة إلى الكائن الحالي الذي كُتبت الشيفرة ضمنه، فهي في مثالنا تكافئ الكائن person. لماذا إذًا لا نستخدم person وحسب؟ في الواقع، لا فائدة من استخدام الكلمة this كثيرًا في حال أنشأت كائن حرفي وحيد، لكن إن أنشأت أكثر من كائن حرفي، يساعدك ذلك في استخدام نفس تعريف التابع لكل كائن أنشأته.

لنوضح ذلك من خلال المثال التالي:

const person1 = {
  name: "Chris",
  introduceSelf() {
    console.log(`Hi! I'm ${this.name}.`);
  },
};

const person2 = {
  name: "Deepti",
  introduceSelf() {
    console.log(`Hi! I'm ${this.name}.`);
  },
};

في هذه الحالة، ينتج عن تنفيذ التعليمة ()person1.introduceSelf الخرج التالي "Hi! I'm Chris."، بينما ينتج عن تطبيق نفس التابع على كائن آخر ()person2.introduceSelf من نفس النوع خرج آخر مناسب للكائن "Hi! I'm Deepti". وعلى الرغم من أن الشيفرة نفسها في الحالتين، لكن أهميتها لن تظهر عند كتابة كائنات حرفية يدويًا، بل عندما تبدأ باستخدام الدوال البانية constructors ﻹنشاء أكثر من كائن من تعريف واحد له وهذا ما نناقشه تاليًا.

مقدمة إلى الدوال البانية Constructors

لا بأس باستخدام الكائنات الحرفية عندما تريد استخدام كائن واحد، لكن إن كان عليك إنشاء أكثر من عنصر سيغدو استخدام العناصر الحرفية غير ملائم. إذ علينا كتابة نفس الشيفرة لكل كائن على حدى، وإن كان علينا تغيير بعض الخاصيات كإضافة الخاصية height، لا بد من تذكرّ تغيير كل الكائنات التي أنشأتها.

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

إليك نسخة أولى من الشكل الذي نتحدث عنه وهو على شكل دالة:

function createPerson(name) {
  const obj = {};
  obj.name = name;
  obj.introduceSelf = function () {
    console.log(`Hi! I'm ${this.name}.`);
  };
  return obj;
}

تُنشئ الدالة وتعيد كائنًا جديدًا في كل مرة نستدعيها، ويضم هذا الكائن عضوين هما:

  • الخاصية: name.
  • التابع: ()introduceSelf.

تأخذ الدالة ()createPersonمعاملًا وحيدًا هو name لضبط قيمة الخاصية name، لكن تبقى قيمة التابع ()introduceSelf نفسها لجميع الكائنات التي تنشأها الدالة. ويُعد هذا اﻷسلوب من أكثر الطرق شيوعًا في إنشاء الكائنات.

بإمكانك اﻵن إنشاء العدد الذي تريده من الكائنات، بإعادة استخدام الدالة:

const salva = createPerson("Salva");
salva.name;
salva.introduceSelf();
// "Hi! I'm Salva."

const frankie = createPerson("Frankie");
frankie.name;
frankie.introduceSelf();
// "Hi! I'm Frankie."

ستعمل الشيفرة السابقة جيدًا لكنها طويلة بعض الشيء. إذ أنشأنا كائنًا فارغًا ثم هيأناه وأعدناه، لكن الطريقة اﻷفضل تقضي باستخدام دالة بانية، وهي دالة نستدعيها باستخدام الكلمة المحجوزة new. وعندما نستدعي دالة بانية فإنها:

  • تنشئ كائنًا جديدًا.
  • تربط this بالكائن الجديد، وستتمكن عندها من اﻹشارة إلى الكائن الجديد من خلالها ضمن شيفرة الدالة البانية.
  • تعيد الكائن الجديد.

تبدأ أسماء الدوال البانية عادة بحرف كبير وتسمى باسم نوع الكائن الذي تنشؤه، لهذا سنعيد كتابة مثالنا ليصبح بالشكل التالي:

function Person(name) {
  this.name = name;
  this.introduceSelf = function () {
    console.log(`Hi! I'm ${this.name}.`);
  };
}

ولكي نستدعي الدالة ()Person كدالة بانية نستخدم الكلمة new:

const salva = new Person("Salva");
salva.name;
salva.introduceSelf();
// "Hi! I'm Salva."

const frankie = new Person("Frankie");
frankie.name;
frankie.introduceSelf();
// "Hi! I'm Frankie."

لقد استخدمت الكائنات كثيرًا فيما مضى

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

فعندما تستخدم التابع النص كما في المثال التالي:

myString.split(",");

فأنت تستخدم التوابع التي يوفرّها الكائن String. وفي كل مرة تنشئ فيها سلسلة نصية في شيفرتك، سيتولد هذا النص تلقائيًا كنسخة عن الكائن String، ويشترك معه بالعدبد من الخاصيات والتوابع. وعندما تحاول الوصول إلى كائن المستند باستخدام شيفرة كالتالي:

const myDiv = document.createElement("div");
const myVideo = document.querySelector("video");

فأنت تستخدم في الواقع التوابع التي يقدّمها الكائن أو الواجهة البرمجية Document، وعند كل تحميل لصفحة الويب تُنشئ نسخة جديدة عن هذا الكائن تُدعى document تمثّل هيكلية الصفحة بأكملها ومحتواها وغير ذلك من الميزات مثل عناوين URL. أي باختصار، سيمتلك الكائن الذي أنشأته عدة توابع وخاصيات يشترك فيها مع الكائن الأساسي Document.

وهذا الأمر مشابه للكثير من الكائنات والواجهات البرمجية المضمنة في لغة جافا سكريبت مثل المصفوفات Array والمكتبة Math.

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

const myNotification = new Notification("Hello!");

الخلاصة

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

ترجمة -وبتصرف- للمقال JavaScript object basics

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...