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

لوحة المتصدرين

  1. محمد فواز عرابي

    • نقاط

      2

    • المساهمات

      71


المحتوى الأكثر حصولًا على سمعة جيدة

المحتوى الأعلى تقييمًا في 06/12/15 in مقالات البرمجة

  1. في سلسلة من عدة أجزاء سنناقش موضوعًا نظريًّا يُعتبر من أساسيّات هندسة البرامج، وهو أنماط التصميم (Design Patterns)، وسنعتمد لغة JavaScript في نقاشنا لتصاعد شعبيّتها ومرونتها التي تسمح لنا ببناء مشاريعنا وفق أنماط متنوّعة مما سيُسهّل علينا شرح موضوع السّلسلة نمط المُشيِّد (Constructor)يشيع استخدام المُشيّدات في اللغات الكائنيّة التّوجّه، حيث تُستخدم لإنشاء نُسخ (instances) من الأصناف (classes)، ومع أنّ JavaScript ليست لغةً كائنيّة التّوجّه بالمعنى التّقليديّ، إلّا أنّها تسمح بإنشاء نُسخ عن كائنات باستخدام بالمُشيّدات، ويمكن لأيّ دالّة أن تُستخدم كمُشيّد، وذلك بأن نُسبقها بالكلمة new، ولتوضيح هذا النّمط سنقوم بإنشاء مُنبّه (كالّذي تضبطه للاستيقاظ في هاتفك) يمكن ضبطه إلى تاريخ ووقت معيّنين ثمّ تفعيله أو تعطيله حسب الرّغبة: function Alarm(when) { this.setAt = when; this.enable = function() { var startAfter = new Date(this.setAt) - new Date; console.log("Alarm will wake you up after " + startAfter/1000 + " seconds"); this.timeout = setTimeout(function() { console.log("Wake up!"); }, startAfter); } this.disable = function() { if (this.timeout) { clearTimeout(this.timeout); delete this.timeout; console.log("Alarm diabled"); } } } var a = new Alarm("2015-03-19 5:58 PM"); a.enable() // Alarm will wake you up after 8.982 seconds // After a few seconds: // Wake up!في المثال السّابق نُسمّي الدّالة Alarm()‎ مُشيّدًا (constructor)، والكائن a نُسخة (instance). لاحظ أنّ Alarm في المثال السّابق ليست سوى دالّة (function)، فهي ليست صنفًا كما في لغات أخرى مثل Java وC++، إذ تُعتبر الدّوال في JavaScript مكوّنًا من الدّرجة الأولى وتُعامل كما يُعامل أيّ كائن، وهكذا يمكن استخدامها كمشيّد لكائن آخر ممّا يسمح بمحاكاة مفهوم الأصناف الّذي لم يُضَف إلّا مؤخّرًا في JavaScript. من عيوب المِثال السّابق إسناد الدّوال الّتي ستعمل عمل الوظائف (methods) إلى النُسخة ذاتها عند إنشائها، وهذا يعني تكرار محتوى الدّوال في الذّاكرة مع كلّ نسخة جديدة من الكائن Alarm، بينما يمكننا توفير هذا الاستهلاك غير المُبرّر للذّاكرة بإسناد الدّوال إلى النّموذج البدئيّ للكائن (أي إلى Alarm.prototype) مما يسمح بمشاركتها بين كل نسخ الكائن، لتقوم الآلة الافتراضيّة بتنفيذ النّصّ البرمجيّ للدّالّة ذاتها بسياق النّسخة (instance context) الّتي استدعت الدّالة، أي إنّ this تُشير ضمن الدّالة عند تنفيذها إلى النُسخةَ المنشأة وليس الصّنف؛ بالطّبع ليس من المرغوب تطبيق الفكرة ذاتها على المُتغيّرات الأخرى مثل setAt، لأنّه من البديهيّ أن تختلف قيمتها بين نسخة وأخرى. لنُعد كتابة المثال السّابق بصورة أفضل: function Alarm(when) { this.setAt = when; } Alarm.prototype.enable = function() { var startAfter = new Date(this.setAt) - new Date; console.log("Alarm will wake you up after " + startAfter/1000 + " seconds"); this.timeout = setTimeout(function() { console.log("Wake up!"); }, startAfter); } Alarm.prototype.disable = function() { if (this.timeout) { clearTimeout(this.timeout); delete this.timeout; console.log("Alarm diabled"); } } var a = new Alarm("2015-03-19 6:21 PM"); a.enable(); // Alarm will wake you up after 30.243 seconds // After 30 seconds... // Wake up!هذا الأسلوب في إنشاء الأصناف شائع جدًّا، وهو يتطلّب فهمًا دقيقًا لآليّة الوراثة في JavaScript؛ إذ يُبنى كلّ كائنٍ فيها على كائن آخر يُسمّى النّموذج البدئيّ (prototype)، ويقوم هذا الكائن الأخير على كائن ثالث أعلى منه في السّلسلة هو نموذجه البدئيّ، وهكذا حتّى نصل إلى null الّذي ليس له نموذج بدئيّ بحسب تعريف اللّغة. في مثالنا السّابق الكائن Alarm.prototype هو النّموذج البدئيّ للكائن a، وهذا يعني أنّ كل الخواصّ المُسندة إلى Alarm.prototype وما فوقه ستكون مُتاحة للكائن a، ولو كتابنا برنامجًا مُشابهًا بـJava لقُلنا إنّ Alarm صنفٌ وإنّ a نُسخة عن هذا الصّنف (instance). عندما نحاول الوصول إلى الخاصّة a.setAt، فإنّ مُفسِّر JavaScript يبدأ بالبحث عن هذه الخاصّة من أدنى سلسلة الوراثة، أي من الكائن a ذاته، فإنّ وجدها قرأها وأعاد قيمتها، وإلّا تابع البحث صعودًا إلى النّموذج البدئيّ وهكذا... وطبيعة JavaScript هذه هي ما سمح لنا بإسناد الوظيفتين enable وdisable إلى Alarm.prototype مطمئنّين إلى أنّها ستكون مُتاحة عند قراءة a.enable()‎ وa.disable()‎. يمكن التأكّد من النّموذج البدئيّ للكائن a كما يلي: Object.getPrototypeOf(a) == Alarm.prototype; // trueالأصناف في ECMAScript 6يُقدّم الإصدار الأحدث من JavaScript مفهوم الأصناف (classes) بصورته التّقليديّة المعروفة في اللّغات الأخرى، إلّا أنّه ليس سوى أسلوب آخر لصياغة النّماذج البدئيّة (أو ما يُسمّى syntactic sugar)، وهذا يعني أنّه نموذج الوراثة في JavaScript لم يتغيّر. يمكننا إعادة كتابة المثال السّابق بصياغة الأصناف في ES6 كما يلي: class Alarm { constructor(when) { this.startAt = when; } enable() { var startAfter = new Date(this.setAt) - new Date; console.log("Alarm will wake you up after " + startAfter/1000 + " seconds"); this.timeout = setTimeout(function() { console.log("Wake up!"); }, startAfter); } disable() { if (this.timeout) { clearTimeout(this.timeout); delete this.timeout; console.log("Alarm diabled"); } } }وستُسند الدّوال enable()‎ وdisable()‎ إلى Alarm.prototype تمامًا كما في المثال الذي سبقه. يُذكر أنّ استخدام new ليست الطّريقة الوحيدة لتشييد الكائنات، إذ يمكن استخدام الوظيفة Object.create()‎ لتُعطي النّتيجة ذاتها: var a = Object.create(Alarm.prototype); Object.getPrototypeOf(a) == Alarm.prototype; // trueإسناد الخواصّ إلى الكائنات‏JavaScript لغة ديناميكية، وهذا يعني أنّه يمكن إضافة وحذف الخواصّ من الكائنات وتعديل نماذجها البدئيّة أثناء التّنفيذ، وهذا ما يمنحها القسم الأكبر من مرونتها ويجعلها مناسبة للاستخدام في بيئة مُعقّدة مثل بيئة الويب، وليس من الغرابة أن توفّر اللّغة وسائل متعدّدة لإسناد الخصائص إلى الكائنات لتلبية الحاجات المتنوّعة لتطبيقات الويب. ماذا لو أردنا تغيير قيمة المنبّه في مثالنا السّابق بعد تفعيله؟ لربّما ترادونا للوهلة الأولى إمكانيّة تغيير قيمة الخاصّة setAt بالطّريقة التّقليدية: a.setAt = "2016-03-03 03:03 PM"; // or a["setAt"] = "2016-03-03 03:03 PM";لكنّ نتيجة هذا الفعل لن تكون كما يُتوقّع، فلو عدنا للمثال السابق وتمعّنا في خواصّه، للاحظنا عيبًا في كيفيّة عمل المُنبّه، إذ إنّ الخاصّة setAt مكشوفة ويمكن تغيير قيمتها في أيّ وقت، حتى بعد تفعيل المُنبّه، إلّا أنّ تغييرها بعدئذٍ لن يغيّر اللّحظة الحقيقيّة الّتي سينطلق فيها المنبّه كما يتضّح لنا عند قراءة النّصّ البرمجيّ، ولذا فنحن هنا أمام حلّين: إمّا منع تغيير قيمة الخاصّة setAt وجعلها للقراءة فقط بعد إنشاء المُنبّه، أو إيقاف المنبّه وإعادة ضبطه في كلّ مرّة تُغيّر فيها قيمة الخاصّة setAt، وكلا الحلّين متاحان إذا كنّا على علم بأساليب إسناد الخصائص في JavaScript. توفّر اللّغة الوظيفة Object.defineProperty()‎‏ الّتي تسمح بتعريف خواصّ لكائن ما مع إمكانيّة التّحكم بتفاصيل هذه الخاصّة، ومن هذه التّفاصيل: هل الخاصّة قابلة للكتابة؟ (writable)هل يجب المرور على هذه الخاصّة عند سرد خواصّ الكائن؟ (enumerable)ما الذي يحدث عند إسناد قيمة للخاصّة؟ (set)ما الذي يحدث عند قراءة قيمة الخاصّة؟ (get)وبهذا يمكننا بسهولة منع تغيير قيمة الخاصّة setAt بعد إسنادها: function Alarm(when) { Object.defineProperty(this, "setAt", { value: when, writable: false }) } Alarm.prototype.enable = function() { var startAfter = new Date(this.setAt) - new Date; console.log("Alarm will wake you up after " + startAfter/1000 + " seconds"); this.timeout = setTimeout(function() { console.log("Wake up!"); }, startAfter); } Alarm.prototype.disable = function() { if (this.timeout) { clearTimeout(this.timeout); delete this.timeout; console.log("Alarm diabled"); } } var a = new Alarm("2015-03-19 7:51 PM"); a.setAt = new Date("2016-03-19"); console.log(a.setAt); // "2015-03-19 7:51 PM"لاحظ أنّ قيمة setAt لم تتغيّر. هذا حلّ جيّد، لكن سيكون من الأفضل السّماح للمُستخدم بتعديل قيمة المنبّه، وعندها سنلجأ لإيقاف المنّبه وإعادة ضبطه كما يلي: function Alarm(when) { var _hidden_value = new Date(when); Object.defineProperty(this, "setAt", { set: function(newValue) { _hidden_value = new Date(newValue); if (this.timeout) { this.disable(); console.log("Alarm changed to " + newValue); console.log("You need to re-enable the alarm for changes to take effect"); } }, get: function() { return _hidden_value; } }) } Alarm.prototype.enable = function() { var startAfter = new Date(this.setAt) - new Date; console.log("Alarm will wake you up after " + startAfter/1000 + " seconds"); this.timeout = setTimeout(function() { console.log("Wake up!"); }, startAfter); } Alarm.prototype.disable = function() { if (this.timeout) { clearTimeout(this.timeout); delete this.timeout; console.log("Alarm diabled"); } } var a = new Alarm("2016-03-03 03:03 PM") a.enable(); // Alarm will wake you up after 30221933.66 seconds a.setAt = "2015-03-19 8:05 PM"; // Alarm changed to 2015-03-19 8:05 PM // You need to re-enable the alarm for changes to take effect a.enable() // Alarm will wake you up after 20.225 seconds // After 20 seconds... // Wake up!لاحظ أنّنا سنحتاج إلى مُتغيّر سرِّيِّ (‎_hidden_value) نُخزّن فيه القيمة الفعليّة لوقت التّنبيه. متى أستخدم هذا النّمط؟نمط المُشيّد لا يحتكر بنية مشروعك عند استخدامه؛ معنى هذا أنّه لا شيء يمنعك من استخدام نمط المُشيّد مع أي نمط آخر عند الحاجة لذلك، فيمكن (بل يشيع كثيرًا) استخدام المُشيّدات ضمن الوحدات (modules) ثمّ تصديرها لاستخدامها من موضع آخر في المشروع، ومثال ذلك أشياء مثل EventEmitter‏ وStreams‏ في Node.js. كما يمكن بناء أنماط أخرى سنتعرّف عليها لاحقًا على أساس المُشيِّدات مثل نمط الكائن المُتفرّد (Singleton) ونمط المُراقِب (Observer pattern). المصادر: شبكة مُطوّري موزيلّا: Inheritance and the prototype chain‏كتاب JavaScript Design Patterns‏ لمؤلّفه Addy Osmani
    1 نقطة
  2. تعلمنا في الجزء السابق أن HTTP بروتوكول على مستوى التطبيقات. حان الوقت لنفهم كيفية استخدام هذا البروتوكول للتواصل بين العميل والخادوم. جلب الأشياء من الويب تذكر أن عميل HTTP (وهو المتصفح عادةً) هو الطرف الذي يبدأ بإرسال الطلب إلى الخادوم. يسمح بروتوكول HTTP للعميل بالتعبير عن نيّته من خلال بضعة مُكوّنات: الرابط (URI)، وأفعال HTTP، وترويساته. تسمية الأشياء تمثّل الروابط (URIs) حجر الأساس في الويب، لأنّها تحل مشكلة مهمّة على مستوى الإنترنت بكاملها، وهي مشكلة منح الأشياء مُعرّفًا تنفرد به على الشّبكة. افترض أنّك سألت شخصًا أن يجلب لك شيئًا ويُحضره إليك، يمكن عدّ الطرق الممكنة للطلب على الأصابع. فعادةً ما تُعرّف الكلمات الّتي تستخدمها الشيء الّذي تريده. بإمكانك أن تطلب من صديقك قائلًا: "اجلب لي الكتاب." قد يجيبك صديقك: "حاضر. ولكن أي كتاب؟". فتقول أنت: "الكتاب في الغرفة الأخرى." فيذهب صديقك إلى الغرفة ويسأل: "أي كتاب قلت؟" فتقول أنت وقد شعرت بالضّيق: "الأخضر!" ليقول صديقك: "تمهّل لحظة... هناك كتابان أخضران في الغرفة!" وعندها تنهض لتحضر الكتاب بنفسك. كان الأمر ليكون أكثر بساطة لو أننا وحدنا طريقة نُحدّد فيها الأشياء بطريقة مميّزة للوصول إليها لاحقًا. إحدى الوسائل الممكنة هي الاعتماد على الذاكرة. كيف نعطي الأوامر لشخص ما ليحضر إلينا الغرض المطلوب؟ لنُنشئ نظامًا لذلك: قص ورقات صغيرة أو استخدم ورق الملاحظات اللّصّاق. ضع هذه الأوراق على الأغراض في الغرفة المجاورة أو على الطّاولة (كالكتب مثلًا). اكتب مُعرّفًا مُميِّزًا على كل ورقة. كرّر العملية في غرفة مجاورة أو على طاولة مجاورة. تذكّر أن هذا النّظام لا يقتصر على أغراضك، بل يمتد ليسمح بنوعٍ من التّواصل بين الأغراض جميعها. طبّقت هذا النّظام على أغراضي، فكتبت على كل قطعة ورق واحدًا من المُعرّفات التالية: myRoom.org/table/book/001 myRoom.org/shelf/book/002 otherRoom.org/cup/001 otherRoom.org/flower/001 otherRoom.org/book/001 أعتقد أنك الآن فهمت ما أقصد. لدينا الآن نظام من اللصاقات الّتي تستخدم لتحديد كل شيء في المكان بدقة. في الويب، نحن نتعامل مع فضاء معلومات واللصاقات المُستخدمة ليست سوى الروابط (URIs). الوصول إلى الأشياء المُسمَّاة شرحنا كيف يُبنى طلب HTTP في المقال السابق من خلال الطرفية. استخدمنا بناءً بسيطًا مع فعل HTTP المُسمّى GET مع ترويسة واحدة: Host. GET / HTTP/1.1 Host: www.opera.com القائمة الكاملة لأفعال HTTP الحالية هي: OPTIONS، GET، HEAD، POST، PUT، DELETE، TRACE، CONNECT. لكل فعل دورٌ مختلف عن الأفعال الأخرى وسنشرح ذلك في المقالات التالية. أكثر الأفعال استخدامًا هو GET، ففي كلّ مرة نُدخل عنوان HTTP في شريط العناوين في المتصفح، فإنّنا نرسل طلب GET إلى خادوم. في عالم الويب يُرسل العميل معلومات أكثر (المزيد من ترويسات HTTP) إلى الخادوم للتفاهم على المطلوب. يستغل الخادوم هذه المعلومات ليُعدّل الجواب بما يناسب الترويسات. تتوفّر أداة عمليّة جدًا في Opera Dragonfly لإنشاء طلبات HTTP مُخصّصة وفحص إجابة الخادوم. يمكنك إيجادها في قسم Network في تبويب "Make Request". هنالك ثلاث مناطق في تبويب Make Request: الرابط (URL): مُعرّف المُحتوى (أو عنوان الويب) متن الطّلب (Request body): ما سيُرسله العميل إلى الخادوم (يُرسل الزّر "Send request" الطّلب إلى الخادوم عبر الشّبكة) الجواب (Response): جواب الخادوم بعد إرسال الطّلب تخصيص طلب HTTP انسخ http://www.opera.com/ إلى قسم URL. انسخ والصق طلب HTTP التّالي إلى قسم "Request body" في تبويب Network. أضفنا الترويسة Accept-Language إلى طلب HTTP السابق. اضغط الزر "Send request". GET / HTTP/1.1 Host: www.opera.com Accept-Language: en سيُجيب خادوم Opera بجواب HTTP مؤلّف من بضع ترويسات يليها محتوى المستند. قد تحتاج إلى تمرير النافذة، لأن الجواب قد يكون طويلًا. لاحظ أن المُستند باللغة الإنكليزيّة، ليس فقط من حيث اللغة الّتي كتب بها، بل أيضًا يُنصّ على ذلك صراحةً في خاصّة lang على ال عنصر html: <!DOCTYPE html> <html lang="en"> لنُجرّب الفرنسيّة: GET / HTTP/1.1 Host: www.opera.com Accept-Language: fr سُيجيب الخادم هذه المرة بالفرنسيّة، ويتبيّن ذلك في نص المستند وفي مصدره: <!DOCTYPE html> <html lang="fr"> لاحظ أنّنا استخدمنا الرابط نفسه بالضّبط (http://www.opera.com/) ولكنّنا تلقّينا إجابتين مُختلفتين لا لشيء إلا لأننا غيّرنا الترويسة Accept-Language. لاحظ أيضًا أنّ الخادوم أجاب بترويسات كثيرة تُعطينا معلوماتٍ عن حالة المحتوى ونوعه... إلخ. يسمح هذا للعميل بتعديل أسلوب معالجة المستند. يمكنك تجربة لغات مُختلفة مثل اليابانية (ja) والألمانية (de)... إلخ. ما الذي يحدث إن طلبنا لغة غير موجودة على الخادوم؟ جرّب مثلًا الصّينيّة (zh): GET / HTTP/1.1 Host: www.opera.com Accept-Language: zh ستتلقى النسخة الإنكليزية من الموقع. هل هذا مُحيّر؟ الحقيقة أنّ هذا الأمر يعتمد على تصميم الموقع ذاته. فقد يُصمم جواب HTTP بأسلوب آخر، كأن يُجيب الخادوم "لا، ما من نسخة صينية من الموقع لدينا"، أو "ليست لدينا نسخة صينية، ولكن لدينا اللغات التالية: ...). ولكن فريق تجربة المستخدمين في Opera قرر إرسال النسخة الإنكليزية من الموقع عند طلب لغة غير مُتوفّرة. المسألة مسألة اختيار، فلا قاعدة تُفرض على المواقع بهذا الشأن. ولهذا السّبب أقوم عادةً بتعليم مُصمّمي تجربة الاستخدام ومُطوّري الواجهات بعض أساسيّات HTTP، فهو بروتوكول يتوسّط التفاعل بين العميل والخادوم، وعليه فإنّ فهمه يُساعد في تصميم تفاعلات ذات معنى عند بناء المواقع، وذلك للبشر والآلات معًا (كالبرامج الّتي تستخدم الواجهات البرمجيّة (APIs) وما شابهها... إلخ). تذكّر URI: نظام لتعريف المعلومات على الشبكة. أفعال HTTP: يتضمّن البروتوكول حاليًا الأفعال التّالية: OPTIONS، GET، HEAD، POST، PUT، DELETE، TRACE، CONNECT. وقد تعرّفنا في هذه المقالة على أكثرها استخدامًا وهو GET. ترويسات HTTP:** الترويسات هي بيانات إضافية يُرسلها العميل لإعطاء معلومات أكثر عن البيانات المُتبادلة بين العميل والخادوم. تُفيد بعض هذه الترويسات الخادوم بحيث يختار الأسلوب الأفضل للإجابة. ترجمة (بشيء من التّصرّف) لمقال HTTP: Let’s GET It On! لصاحبه Karl Dubosy.
    1 نقطة
×
×
  • أضف...