لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 09/30/24 في كل الموقع
-
هل يمكنني تخطي هذا المسار تطبيقات عملية على النماذج النصية الكبيرة LLMs ثم العودة اليه لاحقا بسبب عدم تمكني من التطبيق لسبب ما أم هذا المسار يؤثر بشكل كبير على ما بعده1 نقطة
-
السلام عليكم انا اتبعت كل الخطوات التي فعلها المدرس ولاكن بالنهاية ظهر لي هذا الخطء كررت التعليمات اكثر من مرة من غير اي جدوى1 نقطة
-
لو سمحت انا مش فاهم في السطر دا في جزء ال id او المعرف id بشكل عام و ليه لما عملنا ابديت علي description ظهر update task في النتيجه مكان new task 3 بالتحديد $query = "UPDATE tasks SET description ='Update Task', completed = 1 WHERE id = 10";1 نقطة
-
مرحبا رياض، ان كان السؤال يخص دورة معينة، يفضل طرحه أسفل الدرس فهنالك مساحة مخصصة لتعليقات الطلبة. عموما، في الشيفرة التي ذكرتها، الـ id هو عمود في الجدول يمثل معرفًا فريدًا لكل سجل في هذا الجدول. يكون لكل سجل id مختلف، ويستخدم هذا العمود للتمييز بين السجلات المختلفة. وفي حالتك، يتم استخدام عمود id للتأكد من أنك تقوم بتحديث السجل الصحيح. والجملة WHERE id = 10 تحدد أن عملية التحديث يجب أن تطبق فقط على السجل الذي يكون فيه id يساوي 10. بدون هذه الجملة، قد تؤدي العملية إلى تحديث جميع السجلات في الجدول، وليس السجل المقصود فقط. إجمالا: الكود يقوم بتحديث سجل محدد (السجل الذي يحتوي على id = 10) في جدول tasks. أي أنه يقوم بتعديل قيمة العمود description إلى 'Update Task'، ويقوم بتعديل العمود completed ليصبح 1 (أي اكتمل). عندما يتم تنفيذ جملة التحديث هذه، يتم تغيير قيمة العمود description للسجل ذي id = 10 إلى 'Update Task'. إذا كانت القيمة السابقة لهذا السجل هي 'New Task'، فإن العملية تستبدلها بالقيمة الجديدة 'Update Task'. وبالتالي، عند الاستعلام عن هذا السجل بعد تنفيذ التحديث، ستظهر لك القيمة الجديدة (Update Task)، وليس القيمة القديمة (New Task). هذا ما في الأمر.1 نقطة
-
1 نقطة
-
ليست لغة برمجة تقليدية مثل بايثون أو جافا، بل هي لغة استعلام مخصصة للتعامل مع قواعد البيانات، و تستخدم لإدارة البيانات، بما في ذلك عمليات الإدخال، التعديل، الحذف، والاستعلام عن البيانات من قاعدة البيانات، يعني SQL مصممة خصيصا لإجراء عمليات على البيانات المخزنة في قواعد البيانات العلائقية، و تحتوي على بعض الخصائص الشبيهة بلغة البرمجة، مثل الحلقات والشروط، ولكنها لا تستخدم لتطوير تطبيقات برمجية كاملة بحد ذاتها.1 نقطة
-
فهم تعدد المهام Concurrency يتطلب استيعاب لبعض المفاهيم الأساسية، وصعوبة كتابة الكود تأتي من عدم وضوح الهدف المراد تحقيقه، فتعدد المهام ليس أداة واحدة، بل مجموعة من التقنيات، واختيار التقنية المناسبة يعتمد على ما تريد تحقيقه. أولاً الـ Processes أو العمليات هي عملية مستقلة تمامًا عن باقي العمليات، لها مساحة ذاكرة خاصة بها، وإنشاء عملية جديدة يعني إنشاء برنامج جديد تمامًا. وذلك مناسب للمهام التي تتطلب عزلًا تامًا أو استخدام موارد كبيرة، والمكتبة المستخدمة هي multiprocessing. للتوضيح بمثال بسيط لحساب مربع الأعداد من 1 إلى 5 في عمليات منفصلة: import multiprocessing def square(n): return n * n if __name__ == '__main__': with multiprocessing.Pool(processes=5) as pool: results = pool.map(square, range(1, 6)) print(results) # Output: [1, 4, 9, 16, 25] المفهوم الثاني هو الخيوط Threads وهي خيوط متعددة تعمل ضمن نفس العملية، وتشارك نفس مساحة الذاكرة، وإنشاء خيط أسرع من إنشاء عملية، لكنها تشارك الموارد. وهي مناسبة للمهام التي تتطلب سرعة عالية وتشارك البيانات، لكن يجب الانتباه إلى مشاكل التزامن (Race Conditions). والمكتبة المستخدمة هي threading. وإليك مثال بسيط لطباعة رسائل من خيوط مختلفة: import threading import time def print_message(message): for i in range(5): print(f"Thread {threading.current_thread().name}: {message}") time.sleep(1) if __name__ == '__main__': thread1 = threading.Thread(target=print_message, args=("Hello",)) thread2 = threading.Thread(target=print_message, args=("World",)) thread1.start() thread2.start() thread1.join() thread2.join() المفهوم الثالث هو المهام المتزامنة Coroutine/Asyncio ولا تنشئ عمليات أو خيوط جديدة، بل تنتقل بين المهام بشكل غير متزامن باستخدام الكلمات المفتاحية async و await، ومناسب للمهام التي تتضمن انتظارًا (مثل طلبات الشبكة) أي عمليات I/O كثيفة (مثل الشبكات، قواعد البيانات)، حيث لا يتم حجب الخيط أثناء الانتظار. والمكتبة المستخدمة هي asyncio، وكمثال بسيط لطباعة رسائل مع تأخير باستخدام Asyncio: import asyncio async def print_message(message, delay): await asyncio.sleep(delay) print(f"Message: {message}") async def main(): await asyncio.gather( print_message("Hello", 2), print_message("World", 1) ) asyncio.run(main()) بالتالي عليك تحديد ما الذي تريد تحقيقه بتعدد المهام؟ هل تريد تسريع عملية حسابية؟ هل تريد التعامل مع طلبات متعددة من الشبكة؟ فالعمليات مناسبة للمهام المستقلة، الخيوط للمهام التي تشارك البيانات، والمهام المتزامنة للمهام التي تتضمن انتظارًا. ولا تحاول كتابة برنامج معقد من البداية، ابدأ بمثال بسيط وفهم كيفية عمله قبل إضافة تعقيدات.1 نقطة
-
أثناء تثبيت Postgres هل قمت بكتابة باسورد؟ عليك كتابته وليس الباسورد الخاص بـ pgAdmin فذلك هو ال Master password للوصول لسيرفرات قاعدة البيانات وليس باسورد قاعدة البيانات نفسها، في حال قمت بكتابة نفس الباسورد فلا مشكلة. المستخدم الإفتراضي لقاعدة البيانات هو postgres عليك كتابة كلمة المرور التي قمت بتحديدها. إن لم تتذكرها، قم بكتابة التالي في psql لتعيين كلمة مرور: ALTER USER postgres WITH PASSWORD 'admin' تستطيع تعديل admin إلى كلمة تريدها. بعد ذلك قم بإعادة تشغيل Postgres من خلال تنفيذ التالي في CMD أو أي منفذ أوامر وليس من خلال PSQL: net stop postgresql net start postgresql1 نقطة
-
يشير مصطلح prototype إلى اﻵلية التي ترث فيها الكائنات ميزات من بعضها في جافا سكريبت، ويختلف عملها عن الوراثة في غيرها من اللغات كائنية التوجه، وهذا ما سنشرحه في هذا المقال. ننصحك قبل أن تبدأ العمل معنا في هذه السلسلة أن تطلع على: أساسيات HTML. أساسيات عمل CSS أساسيات جافا سكريبت أساسيات البرمجة كائنية التوجه في جافا سكربت كما شرحناه في مقال أساسيات العمل مع الكائنات في جافا سكريبت. سلسلة من اﻷنماط المجرّدة حاول أن تنشئ في طرفية جافا سكريبت في متصفحك الكائن التالي: const myObject = { city: "Madrid", greet() { console.log(`Greetings from ${this.city}`); }, }; myObject.greet(); // Greetings from Madrid يمتلك الكائن خاصية واحدة لتخزين البيانات هي city وتابعًا واحدًا هو ()greet. فإن كتبت اسم الكائن تليه نقطة في الطرفية مثل .myobject، ستعرض الطرفية قائمة بجميع الخاصيات التي يمتلكها العنصر. وسترى إضافة إلى الخاصية city والتابع ()greet، الكثير من الخاصيات اﻷخرى! __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__ __proto__ city constructor greet hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf جرّب الوصول إلى إحداها: myObject.toString(); // "[object Object]" لقد نجح اﻷمر (حتى لو لم يكن واضحًا لك بالضبط ما الذي يفعله التابع ()toString هنا). فما قصة هذه الخاصيات اﻹضافية، ومن أين أتت؟ في الواقع يملك كل كائن في جافا سكريبت خاصية مضمنة تُدعى prototype وهي بحد ذاتها كائن أيضًا ويضم بدوره خاصية أو كائن مجرّد إن صح التعبير، مما يوّلد ما يُدعى سلسلة prototype chain. تنتهي السلسلة عند الوصول إلى كائن قيمة الخاصية prototype له تساوي null. ملاحظة:لا تُدعى الخاصية التي تشير إلى prototype بالاسم prototype، إذ ليس لها اسم معياري لكنها تُكتب بالممارسة العملية بالشكل _proto_. وتُعد الطريقة المعيارية للوصول إلى الخاصية prototype هي استخدام التابع ()Object.getPrototypeOf. عندما تحاول الوصول إلى إحدى خاصيات كائن، ولم يُعثر على الخاصية في الكائن نفسه، يجري البحث عنها ضمن الكائن prototype، وإن لم يُعثر عليها يجري البحث مجددًا ضمن الكائن prototype للكائن prototype حتى نهاية السلسلة، فإن لم يجدها، سيُعيد القيمة undefined. فعندما تُنفّذ التعليمة ()myObject.toString: يبحث المتصفح عن التابع toString ضمن الكائن myObject. إن لم يجده، سيبحث عنه في الكائن prototype للكائن myObject. يجده هناك ويستدعيه. لكن ما هو prototype للكائن myObject؟ لمعرفة ذلك، يمكننا استخدام الأمر: Object.getPrototypeOf(myObject); // Object { } سنجد أن prototype هو كائن يُدعى Object.prototype، وهو أبسط الكائنات prototype، وتمتلكه جميع الكائنات افتراضيًا، والكائن prototype الخاص به هو null. لذا يقع هذا الكائن في نهاية سلسلة كائنات prototype. لكن لا يمثل Object.prototype دائمًا prototype لكل كائن، جرّب ما يلي لترى: const myDate = new Date(); let object = myDate; do { object = Object.getPrototypeOf(object); console.log(object); } while (object); // Date.prototype // Object { } // null تنشئ الشيفرة السابقة كائن من النوع Date، ثم تنتقل ضمن سلسلة كائنات prototype الخاصة به وتسجل أسماء هذه الكائنات. وتظهر أن النوع المجرد للكائن myDate هو Date.prototype وprototype الخاص بهذا اﻷخير هو Object.prototype. وعندما تستدعي توابع مثل ()mydate2.getMonth، فأنت تستدعي في واقع اﻷمر توابع معرّفة ضمن النوع Date.prototype. إخفاء الخاصيات ما الذي يحدث إن عرّفت خاصية في كائن وكانت هناك خاصية معرّفة بنفس الاسم ضمن الكائن prototype له؟ ألق نظرة على الشيفرة التالية: const myDate = new Date(1995, 11, 17); console.log(myDate.getYear()); // 95 myDate.getYear = function () { console.log("something else!"); }; myDate.getYear(); // 'something else!' لا بد أن تكون النتيجة التي حصلت عليها متوقعة. ووفقًا لوصف سلسلة الكائنات المجردة، سيبحث المتصفح عن الخاصية ()getYear ضمن خاصيات myDate التي تحمل هذا الاسم، ولا يتحقق من خاصيات الكائن المجرد إلا في الحالة التي لم نعرّف فيها هذه الخاصية. لهذا عندما أضفنا التابع ()getYear حرفيًا إلى الكائن myDate تُستدعى هذه النسخة مباشرة ويُعرف هذا اﻷمر بإخفاء الخاصية shadowing. إعداد كائنات prototype هناك طرق مختلفة ﻹعداد وضبط هذه الكائنات في جافا سكريبت، و سنناقش هنا طريقتان: الأولى باستخدام ()Object.create والثانية استخدام الدوال البانية constructors. استخدام التابع ()Object.create يُنشئ التابع ()Object.create كائنًا جديدًا ويسمح لك بتخصيص كائن ليصبح prototype الجديد الخاص به، إليك مثالًا: const personPrototype = { greet() { console.log("hello!"); }, }; const carl = Object.create(personPrototype); carl.greet(); // hello! أنشأنا في الشيفرة السابقة كائنًا باسم personPrototype، يمتلك التابع ()greet، ثم أنشأنا كائنًا جديدًا باستخدام التابع ()Object.create وجعلنا personPrototype كائن prototype له. وبالتالي نستطيع اﻵن استدعاء التابع ()greet من خلال الكائن الجديد، لأن كائن prototype قد زوّده به. استخدام الدالة البانية تمتلك جميع الدوال في جافا سكريبت خاصية تًدعى prototype. وعندما تستدعي الدالة على شكل دالة بانية، تُضبط تلك الخاصية لتكون prototype للكائن المبني حديثًا (داخل الخاصية التي تُدعى _proto_ تقليديًا). لهذا، وعندما نضبط القيمة prototype للدالة البانية، نضمن أن الكائنات التي تُنشئها هذه الدالة تمتلك كائن prototype: const personPrototype = { greet() { console.log(`hello, my name is ${this.name}!`); }, }; function Person(name) { this.name = name; } Object.assign(Person.prototype, personPrototype); // or // Person.prototype.greet = personPrototype.greet; لقد أنشأنا هنا: كائنًا بالاسم personPrototype يمتلك التابع ()greet. دالة بانية ()Person تهيئ اسم الشخص الذي نحييه. وضعنا بعد ذلك التوابع المعرّفة ضمن الكائن personPrototype ضمن الكائن prototype للدالة البانية باستخدام التابع Object.assign. وهكذا ستمتلك الكائنات المبنية باستخدام الدالة ()Person prototype Person.prototype الذي يضم تلقائيًا التابع greet. const reuben = new Person("Reuben"); reuben.greet(); // hello, my name is Reuben! يشرح هذا أيضًا ما قلناه سابقًا بأن الكائن prototype للكائن myDate هو Date.prototype، إذ يمثّل الخاصية للبانية Date. الخاصيات المملوكة Own properties يمتلك الكائن الذي أنشأناه باستخدام البانية ()Person خاصيتين: الخاصية name التي ضبطنا قيمتها باستخدام الدالة البانية، لهذا تظهر مباشرة ضم الكائن Person. التابع ()greetالذي ضبُط من خلال الكائن prototype. من الشائع أن تشاهد هذا الأسلوب الذي تُعرّف فيه التوابع ضمن كائنات prototype، وتُعرّف فيه خاصيات البيانات ضمن الدوال البانية. ذلك أن التوابع تبقى نفسها عادة لجميع الكائنات التي ننشئها، لكننا غالبا ما نريد أن يأخذ كل كائن قيم مخصصة لخاصيات البيانات (كأن يكون لكل شخص اسم خاص). تُدعى الخاصيات التي تُعرّف مباشرة ضمن الكائن مثل الخاصية name بالخاصيات المملوكة Own Property، وبإمكانك التحقق من كون الخاصية مملوكة باستخدام التابع الساكن ()Object.hasOwn: const irma = new Person("Irma"); console.log(Object.hasOwn(irma, "name")); // true console.log(Object.hasOwn(irma, "greet")); // false ملاحظة: كما تستطيع استخدام التابع غير الساكن ()Object.hasOwnProperty في هذه الحالة لكننا ننصح باستخدام التابع الساكن ما أمكن. الكائنات prototype والوراثة تُعد كائنات prototype ميزة قوية ومرنة في جافا سكريبت، تسمح لك بإعادة استخدام الشيفرة ودمج الكائنات. وهي بالتحديد تدعم نوعًا من الوراثة inheritance، والتي هي ميزة من ميزات البرمجة كائنية التوجه OOP. إذ تسمح الوراثة للمبرمج التعبير عن فكرة مفادها أن بعض الكائنات هي نسخ أكثر تخصيصًا من كائنات أخرى. فلو كنا نبني نموذجًا عن مدرسة، فقد ننشئ كائنات مثل مدرّس أو طالب وكلاهما أشخاص ويمتلكان بعض الميزات المتشابهة كاﻷسماء مثلًا، لكن قد يكون لكل منهما ميزات إضافية تميّزه عن اﻵخر (كأن يكون للمدرس موّاد يدرّسها)، وقد تنجز نفس الميزات بطريقة مختلفة لكل منهما. لهذا نقول في البرمجة كائنية التوجه OOP بأن الطالب والمدرس كائنان يرثان من كائن آخر يُدعى شخص. أما في جافا سكريبت فيمكن للكائنين Professor و Student أن يمتلكا نفس الكائن المجرد Person، ويرثا الخاصيات التي يمتلكها كائن prototype كما يمكن أن نعرّف خاصيات وتوابع جديدة تناسب كل منهما. سنناقش في مقالات لاحقة مفهوم الوراثة إضافة إلى الميزات الأخرى للبرمجة كائنية التوجه وطريقة دعم جافا سكريبت لها. الخلاصة غطينا في مقالنا كائنات prototype في جافا سكريبت، وآلية تكوين سلاسل كائنات prototype التي تسمح للكائنات أن ترث ميزات من بعضها، كما ناقشنا الخاصية prototype وكيفية استخدامها في إضافة توابع إلى الدوال البانية وغيرها من النقاط التي تتعلق بكائنات prototype. ترجمة -وبتصرف- للمقال Object prototpes اقرأ أيضًا المقال السابق: أساسيات العمل مع الكائنات في جافا سكريبت وراثة الأصناف (Class inheritance) في جافاسكربت كيف أتعلم لغة جافا سكريبت من الصفر حتى الاحتراف لغة البرمجة بالكائنات Object-Oriented Programming1 نقطة
-
نلقي نظرة في هذا المقال على الصياغة اﻷساسية لكائن جافا سكريبت كما نستذكر بعض الميزات التي ناقشناها سابقًا لنشدد على حقيقة أن العديد من الميزات التي اطلعنا عليها وتعاملنا معها في لغة جافا سكريبت هي في الواقع كائنات، ابتداءً من المميزات البنيوية للغة مثل المصفوفات إلى الواجهات البرمجية للمتصفحات APIs المبنية على أساس جافا سكريبت. وبإمكانك أيضًا بناء كائنات خاصة بك لتغليف مجموعة من الدوال والمتغيرات ضمن حزمة أكثر فعالية تعمل كحاوية بيانات. ولا بد أن تتفهم طبيعة جافا سكريبت القائمة على الكائنات إن أردت التعمق أكثر في تعلم هذه اللغة، لهذا نزوّدك ضمن هذا المقال والمقالات التالية بمعلومات مهمة عن الكائنات البرمجية وصياغتها بشيء من التفصيل ثم ننتقل إلى شرح طريقة بناء كائنات خاصة بك. ننصحك قبل أن تبدأ العمل معنا في هذه السلسلة أن تطلع على: أساسيات HTML أساسيات عمل CSS أساسيات جافا سكريبت أساسيات الكائنات الكائن 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 اقرأ أيضًا المقال السابق: مدخل إلى اﻷحداث في جافاسكريبت مدخل إلى جافاسكريبت كائنية التوجه (Object-Oriented JavaScript) لغة البرمجة بالكائنات Object-Oriented Programming برمجة الكائنات Objects في جافاسكريبت مختصر البرمجة كائنية التوجه OOP وتطبيقها في بايثون1 نقطة