This translation is incomplete. Please help translate this article from English.
ال Prototypes هي الآلية التي تعتمدها الجافاسكريبت للوراثة، بمعنى آخر، يمكننا من خلال ال Prototypes جعل كائن يرث مميزات كائن آخر. وهي تعمل بشكل مختلف عن مفهوم آلية الوراثة في اللغات الغرضية التوجة الكلاسيكية. في هذا الدرس سنكتشف هذا الاختلاف، وسنرى كيف تعمل ال prototype chains، وسنلقي نظرة حول كيفية إضافة وظيفة معينة لـ constructors موجودة عن طريق الخاصية prototype.
| المتطلبات الأساسية : | دراية لا بأس بها بخصوص الحاسوب، الإلمام بمبادئ وأساسيات الـ HTML وCSS وجافاسكريبت (راجع First steps and Building blocks) وأساسيات بناء الكائنات في الجافاسكريبت OOJS (راجع Introduction to objects). |
|---|---|
| الهدف : | فهم جافاسكريبت object prototypes، وكيف تعمل ال prototype chains، وكيفية إضافة وظائف جديدة على الخاصية prototype. |
الجافا سكريبت لغة تعتمد على ال prototype ؟؟
غالبا ما توصف جافاسكريبت، على أنها لغة تعتمد على البروتوتايب (prototype-based language)، يحتوي كل كائن فيها على الكائن prototype. والذي يعمل كقالب للكائن، يقوم بوراثة خصائصه ووظائفه، الكائن prototype في كائن يمكنه أيضا أن يحتوي على الكائن prototype، ويرث خصائصه ووظائفه أيضا، هكذا ودواليك، وغالبا ما يشار إلى هذا السلوك بال prototype chain (سلسلة النمودج)، وهذا يشرح كيف أن كائنات مختلفة لديها خصائص ووظائف مُعَرَّفة في كائنات أخرى، متاحة لها.
حسنا، حتى نكون أكثر دقة، الخصائص والوظائف محددة في الـ constructor functions لل Object وليس في الـ object instances (مثيلات الكائن).
بالنسبة للبرمجة الكلاسيكية غرضية التوجه classic OOP، يتم تعريف الكلاسات، بعد أن يتم إنشاء object instances، ويتم نسخ كافة الخصائص والوظائف المحددة في الكلاس فوق ال instance.
في الجافاسكريبت، الأمر مختلف، حيث يتم تكوين رابط بين الـ object instance والـ constructor الخاص به. يمكن تشبيه الأمر بتكوين (حلقة في سلسلة ال prototype)، ويتم الوصول إلى الخصائص والوظائف من الـ constructor، من خلال التنقل صعودا في السلسلة.
دعونا نلقي نظرة على هذا المثال ليصبح الأمر أكثر وضوحا.
فهم ال prototype objects
لنَـعُد إلى المثال الذي انتهينا من كتابتة Person() constructor، قم بتحميل المثال التالي في المتصفح الخاص بك، إذا لم يكن لديك المثال الذي عملنا عليه في الدرس السابق استخدم المثال الخاص بنا oojs-class-further-exercises.html او (قم بنسخ source code).
في هذا المثال قمنا بتعريف constructor function، كالتالي :
function Person(first, last, age, gender, interests) {
// property and method definitions
};
ثم قمنا بإنشاء object instance منه هكذا :
var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
إذا قمت بكتابة ".person1" في الجافاسكريبت console ستلاحظ ان المتصفح يقوم بالاكمال التلقائي ويظهر اسماء الاعضاء الموجودة في هذا الكائن.

في هذه القائمة، نرى الاعضاء المعرفة في الكائن prototype الخاص ب person1، الذي هو ال Person() constructor، ومكوناته هي name, age, gender, interests, bio و greeting. كما نرى ايضا بعض الأعضاء الآخرين مثل watch ،valueOf،...الخ. وهي معرفة في الكائن prototype لل Person() constructor ، الذي هو Object،
وهذا يوضح لنا، كيف يعمل ال prototype chain.

فماذا سيحدث إذا قمنا باستدعاء وظيفة على ال person1، وهي احدى الوظائف المعرفة في ال Object؟ على سبيل المثال:
person1.valueOf()
ببساطة فهذه الوظيفة ستعود بقيمة الكائن الذي تم استدعاؤه، حاول تجربته وانظر ما الذي سيحدث.
في هذه الحالة:
- في البداية سيقوم المتصفح بالتحقق ما اذا كان الكائن person1 يحتوي على الوظيفة ()valueOf
- اذا لم يتم ذالك، سيقوم المتصفح بالتحقق ما اذا كان الكائن prototype للكائن person1 الذي هو ال (Person) يحتوي على الوظيفة ()valueOf
- اذا لم يتم ذالك ايضا، سيقوم المتصفح بالتحقق ما اذا كان الكائن prototype لل Person() constructor الذي هو (Object) يحتوي على الوظيفة ()valueOf،
اذا تم، سيستدعيه، وكل شئ على ما يرام.
تذكير: الخصائص والوظائف لا يتم نسخها من كائن إلى آخر في ال ptototype chain —
يتم الوصول إليها بالتنقل صعودا في السلسلة كما هو موضح أعلاه.
ملاحظة: رسميا لا توجد طريقة للوصول مباشرة الى الكائن prototype داخل كائن. "الروابط" بين العناصر موجودة في السلسلة ويتم تعريفها في خاصية داخلية، يشار إليها ب [[prototype]] في مواصفات لغة جافا سكريبت (see ECMAScript). ومع ذلك فمعظم المتصفحات الحديثة تحتوي على خاصية متاحة لها تسمى __proto__ (لديها 2 underscores في كلا جوانبها), والتي تحتوي على الكائن prototype في كائن. على سبيل المثال جرب:
person1.__proto__ and person1.__proto__.__proto__
لمشاهدة ما يشبه السلسلة في التعليمات البرمجية!
الخاصية prototype : اين يتم تعريف الأعضاء الموروثة
اذا، أين يتم تعريف الخصائص والوظائف الموروثة؟
إذا نظرتم إلى صفحة مرجع ال Object سترون هناك في الجانب الأيسر عددا كبيرا من الخصائص والوظائف المدرجة. عدد كبير من الأعضاء الموروثة كما شاهدنا اعلاه في الصورة التوضيحية، متاحة للكائن person1، البعض منها موروثة، والبعض لا، لم هذا؟
الجواب، أن تلك الموروثة هي تلك المعرفة في الخاصية prototype (يمكن استدعاؤها تحت ال namespace)، اي تلك التي تبدأ ب .Object.prototype وليست تلك التي تبدأ بمجرد .Object، قيمة الخاصية prototype هي كائن، والتي هي في الاساس حزمة او وعاء لتخزين الخصائص والوظائف التي نريدها أن تكون موروثة من قبل الكائنات اسفل ال prototype chain.
حتى ال ()Object.prototype.watch(), Object.prototype.valueOf, متاحة لأي نوع من أنواع الكائنات التي ترث من ال ()Object بما في ذلك ال object instances الجديدة التي يتم إنشاؤها من ال constructor.
()Object.is() ،Object.keys، والأعضاء الاخرى غير معرفة داخل حزمة ال prototype وهي وغير موروثة بواسطة ال object instances أو أنواع الكائنات التي ترث من ال ()Object. هي خصائص ووظائف متاحة فقط لل Object() constructor نفسه.
ملاحظة: يبدو هذا غريبا - كيف يمكن أن يكون لديك وظيفة معرفة في ال constructor، الذي هو في حد ذاته function؟ حسنا، هي ايضا function من النوع object - راجع مرجع ال Function() constructor حتى تتاكد بنفسك.
يمكنك التحقق من خصائص ال prototype الموجودة بنفسك، ارجع الى مثالنا السابق وحاول ادخال السطر التالي في الجافاسكريبت console:
Person.prototype
المخرجات لن تظهر لك الكثير, لاننا حتى الان، لم تقم بتعريف أي شيء في ال prototype داخل ال constructor الخاص بنا!، بشكل افتراضي، دائماً يبدأ ال prototype داخل ال constructor فارغا. الآن جرب ما يلي:
Object.prototype
سترى عددا كبيرا من الوظائف المعرفة في الخاصية prototype الخاصة ب Object، ثم التي تتوفر على على الكائنات التي ترث من ال Object، كما وضحنا في وقت سابق.
سترى أمثلة أخرى لوراثة ال prototype chain في جميع أنحاء جافا سكريبت، حاول البحث في الوظائف والخصائص المعرفة في prototype الكائنات العامة String, Date, Number, و Array على سبيل المثال. كلها تحتوي على عدد من الأعضاء المعرفة في ال prototype الخاص بها، على سبيل المثال عندما نقوم بإنشاء سلسلة نصية (string)، مثل هذه:
var myString = 'This is my string.';
myString، فور انشائها سيتاح لها العديد من الوظائف المفيدة، مثل ()split(), indexOf(), replace, ...الخ
هام: الخاصية prototype هي واحدة من أكثر الاجزاء ارباكا لجافا سكريبت، قد تعتقد أن هذا يشير الى الكائن prototype للكائن الحالي، لكن ليس هذا ما يحدث (تذكر ان هذا كائن داخلي يمكن الوصول إليه بواسطة ال __proto__ ). ال prototype هي خاصية تحتوي على الكائن الذي ستعرف فيه الاعضاء التي تريدها أن تكون موروثة.
إعادة النظر في الوظيفة ()create
راينا سابقا كيف يمكن استخدام الوظيفة ()Object.create لانشاء object instance.
على سبيل المثال، حاول تجربة هذا في console الجافا سكريبت في المثال الخاص بك:
var person2 = Object.create(person1);
ماتفعله الوظيفة ()Create في الواقع هو انشاء كائن جديد، من الكائن prototype المحدد، في هذه الحالة، سيتم إنشاء person2 باستخدام person1 على شكل الكائن prototype، يمكنك التحقق من ذلك عن طريق إدخال ما يلي في ال console :
person2.__proto__
النتيجة: إرجاع الكائن person1.
الخاصية constructor
كل object instance يحتوي على الخاصية constructor والتي تشير إلى ال constructor function الأصلي الذي أنشأ ال instance.
على سبيل المثال، حاول ادخال هذه الأوامر في ال console:
person1.constructor person2.constructor
المفروض على كل منهما العودة لل Person() constructor، كما سيحتوي كل منهما على التعريف الأصلي لهذه ال instances.
خدعة ذكية، يمكنك وضع الاقواس في نهاية الخاصية constructor (مع البرامترات المطلوبة) لانشاء object instance اخر من هذا ال constructor، ال constructor هو في النهاية function، لذالك يمكنك استدعاؤها باستخدام الأقواس، ستحتاج فقط إلى تضمين الكلمة المحجوزة new لتحديد أنك تريد استخدام هذه ال function ك constructor.
حاول ادخال هذا في ال console:
var person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);
الآن حاول الوصول إلى خصائص الكائن الجديد الخاص بك، على سبيل المثال:
person3.name.first person3.age person3.bio()
هذا سيعمل بشكل جيد. وعلى الارجح لن تحتاج لاستخدامه في كثير من الأحيان، ولكن يمكن أن يكون مفيدا حقا، عندما تريد إنشاء instance جديد لا يشير إلى ال constructor الأصلي المتاح.
الخاصية constructor لها استخدامات أخرى كذالك. على سبيل المثال، إذا كان لديك
object instance وترغب في إرجاع اسم ال contructor وهو instance، يمكنك استخدام ما يلي:
instanceName.constructor.name
جرب هذا، على سبيل المثال:
person1.constructor.name
التعديل على ال prototypes
دعونا نلقي نظرة على مثال يقوم بتعديل الخاصية prototype الخاصة بال constructor.
نعود إلى مثالنا oojs-class-further-exercises.html قم بعمل نسخة من الكود التالي source code. اسفل كود الجافاسكريبت، اضف التعليمة البرمجية التالية، وهي خاصة باظافة وظيفة جديدة على constructor الخاصية prototype :
Person.prototype.farewell = function() {
alert(this.name.first + ' has left the building. Bye for now!');
}
احفظ التعليمات البرمجية وقم بتحميل الصفحة في المتصفح، وحاول إدخال ما يلي في حقل المدخلات الخاص بك:
person1.farewell();
ستحصل على رسالة تنبيه معروضة (alert)، تضم اسم الشخص كما هو محدد داخل ال constructor. وهذا مفيد حقاً، ولكن الاكثر افائدة هو أنه سيتم تحديث سلسلة الوراثة باكملها بشكل حيوي، و بشكل تلقائي يتم جعل الوظيفة الجديدة متاحة لجميع ال object instances المشتقة من ال constructor.
لنفكر في هذا للحظة. في التعليمة البرمجية خاصتنا، قمنا بتعريف constructor،
ثم قمنا بانشاء instance object من هذا ال constructor، ثم قمنا باظافة الوظيفة الجديدة على constructor الخاصية prototype.
function Person(first, last, age, gender, interests) {
// property and method definitions
};
var person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']);
Person.prototype.farewell = function() {
alert(this.name.first + ' has left the building. Bye for now!');
}
ولكن الوظيفة ()farewell، لا تزال متاحة في ال person1 object instance — تم تحديث الوظائف المتاحة لها تلقائيا، هذا يثبت ما قلناه سابقا عن ال prototype chain. والمتصفح سيبحث صعودا في السلسلة لإيجاد الوظائف التي لم يتم تعريفها في ال object instance نفسها، بدلا من تلك الوظائف التي يتم نسخها إلى ال instance. وهذا يجعلها قوية جداً، و نظامها الوظيفي قابل للتوسع/للتمدد.
Note: إذا كنت تواجه صعوبة في الحصول على هذا العمل, الق نظرة على المثال الخاص بنا oojs-class-prototype.html (شاهد ايضا running live ).
نادرا ما ستشاهد خصائص معرفة في الخاصية prototype، على سبيل المثال يمكن إضافة خاصية جديدة كالتالي:
Person.prototype.fullName = 'Bob Smith';
ولكن هذه ليست مرنة جداً، حيث ان ال person لا يمكنه استدعاءها، من الافضل القيام بذلك من خلال بناء fullName خارج name.first و name.last:
Person.prototype.fullName = this.name.first + ' ' + this.name.last;
ومع ذالك فهي لن تعمل، في هذه الحالة this ستشير الى النطاق العام (global scope). وليس الى نطاق الفانكشن (function scope). استدعاء هذه الخاصية سيعود ب undefined undefined. سيعمل هذا بشكل جيد في الوظائف التي عرفناها سابقا في ال prototype لانها توجد داخل ال function scope، والتي ستنقل بنجاح إلى نطاق مثيل الكائن (object instance scope). لذا قد تقوم بتعريف خصائص ثابتة (constant properties — أي تلك التي لا تحتاج للتغيير) في ال prototype، ولكن عموما فهي تعمل بشكل أفضل لتعريف خصائص داخل ال constructor.
في الواقع، النمط الشائع إلى حد كبير بخصوص تعريف الكائن، هو ان يتم تحديد الخصائص داخل ال constructor، والوظائف على ال prototype. وهذا يجعل الكود اسهل في القراءة، كما سيحتوي ال constructor على الخصائص المعرفة فقط، ويتم تقسيم الوظائف في بلوكات منفصلة، مثلا:
// Constructor with property definitions
function Test(a,b,c,d) {
// property definitions
};
// First method definition
Test.prototype.x = function () { ... }
// Second method definition
Test.prototype.y = function () { ... }
// etc.
هذا النمط من العمل يمكن ان نراه في Piotr Zalewa's school plan app.
Summary
غطت هذه المادة، جافاسكريبت object prototypes، وكيف يسمح ال prototype object chains للكائنات أن ترث الميزات من بعضها البعض، وكيف يمكن استخدام الخاصية prototype لإضافة وظائف جديدة على ال constructors، وغيرها من الموضوعات ذات الصلة.
في المقال القادم سنرى كيفية تنفيذ الوراثة بين اثنين من الكائنات الخاصة بك.