تُنشّأ الكائنات عادة لتُمَثِّل أشياء من العالم الحقيقي مثل المستخدمين، والطلبات، وغيرها:
let user = {
name: "John",
age: 30
};
يمكن للمستخدم في العالم الحقيقي أن يقوم بعدة تصرفات: مثل اختيار شيء من سلة التسوق، تسجيل الدخول، والخروج …إلخ.
تُمَثَّل هذه التصرفات في لغة JavaScript بإسناد دالة إلى خاصية وتدعى الدالة آنذاك بالتابع (method، أي دالة تابعة لكائن).
أمثلة على الدوال
بدايةً، لنجعل المستخدم user يقول مرحبًا:
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("Hello!");
};
user.sayHi(); // Hello!
Here we’ve just used a Function Expression to create a function and assign it to the property user.sayHi of the object.
Then we can call it as user.sayHi(). The user can now speak!
A function that is a property of an object is called its method.
So, here we’ve got a method sayHi of the object user.
يمكننا أيضًا استخدام دالة معرفة مسبقًا بدلًا من ذلك كما يلي:
let user = {
// ...
};
// أولا، نعرف دالة
function sayHi() {
alert("Hello!");
};
// أضِف الدالة للخاصية لإنشاء تابع
user.sayHi = sayHi;
user.sayHi(); // Hello!
يسمى كتابة الشيفرة البرمجية باستخدام الكائنات للتعبير عن الاشياء «بالبرمجة الشيئية/كائنية» ([object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming)، تُختَصَر إلى "OOP").
OOP هو موضوع كبيرجدًا، فهو علم مشوق ومستقل بذاته. يعلمك كيف تختار الكائنات الصحيحة؟ كيف تنظم التفاعل فيما بينها؟ كما يعد علمًا للهيكلة ويوجد العديد من الكتب الأجنبية الجيدة عن هذا الموضوع مثل كتاب “Design Patterns: Elements of Reusable Object-Oriented Software” للمؤلفين E.Gamma، و R.Helm، و R.Johnson، و J.Vissides أو كتاب “Object-Oriented Analysis and Design with Applications” للمؤلف G.Booch، وغيرهما.
اختصار الدالة
يوجد طريقة أقصر لكتابة الدوال في الكائنات المعرفة تعريفًا مختصرًا باستعمال الأقواس تكون بالشكل التالي:
// يتصرف الكائنان التاليان بالطريقة نفسها
user = {
sayHi: function() {
alert("Hello");
}
};
// يبدو شكل الدالة المختصر أفضل، أليس كذلك؟
user = {
sayHi() { // same as "sayHi: function()"
alert("Hello");
}
};
يمكننا حذف الكلمة "function" وكتابة sayHi() كما هو موضح. حقيقةً، التعبيرين ليسا متطابقين تمامًا، يوجد اختلافات خفية متعلقة بالوراثة في الكائنات (سيتم شرحها لاحقًا)، لكن لا يوجد مشكلة الآن. يفضل استخدام الصياغة الأقصر في كل الحالات تقريبًا.
الكلمة “this” في الدوال
من المتعارف أن الدوال تحتاج للوصول إلى المعلومات المخزنة في الكائن لِتنفذ عملها. مثلًا، قد تحتاج الشيفرة التي بداخل user.sayHi() لِاسم المستخدم user. هنا،
يمكن للدالة استخدام الكلمة this للوصول إلى نسخة الكائن التي استدعتها
أي، قيمة this هي الكائن “قبل النقطة” الذي استُخدِم لاستدعاء الدالة.
مثلًا:
let user = {
name: "John",
age: 30,
sayHi() {
// "this" هو الكائن الحالي"
alert(this.name);
}
};
user.sayHi(); // John
أثناء تنفيذ user.sayHi() هنا، ستكون قيمة this هي الكائن user
عمليًا، يمكن الوصول إلى الكائن بدون استخدام this بالرجوع إليه باستخدام اسم المتغير الخارجي:
let user = {
name: "John",
age: 30,
sayHi() {
alert(user.name); // "user" يدلًا من "this"
}
};
…لكن، لا يمكن الاعتماد على الطريقة السابقة. فإذا قررنا نسخ الكائن user إلى متغير آخر، مثلا: admin = user وغيرنا محتوى user لشيء آخر، فسيتم الدخول إلى الكائن الخطأ كما هو موضح في المثال التالي:
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name ); // يتسبب في خطأ
}
};
let admin = user;
user = null; // تغيير المحتوى لتوضيح الأمر
admin.sayHi(); // TypeError: Cannot read property 'name' of null
إن استخدمنا this.name بدلًا من user.name بداخل alert، فستعمل الشيفرة عملًا صحيحًا.
In JavaScript, keyword this behaves unlike most other programming languages. It can be used in any function, even if it’s not a method of an object.
الكلمة this في JavaScript تتصرف تصرفًا مختلفًا عن باقي اللغات البرمجية. فيمكن استخدامها في أي دالة. انظر إلى المثال التالي، إذ لا يوجد خطأ في الصياغة
function sayHi() {
alert( this.name );
}
تُقَيَّم قيمة this أثناء تنفيذ الشيفرة بالاعتماد على السياق. مثلًا، في المثال التالي، تم تعيين الدالة ذاتها إلى كائنين مختلفين فيصبح لكل منهما قيمة مختلفة لـ “this” أثناء الاستدعاء:
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// استخدام الدالة ذاتها مع كائنين مختلفين
user.f = sayHi;
admin.f = sayHi;
// tلدى الاستدعائين قيمة مختلفة لـ
// "this" التي بداخل الدالة تعني المتغير الذي قبل النقطة
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (يمكن الوصول إلى الدالة عبر الصيغة النقطية أو الأقواس المربعة – لا يوجد مشكلة في ذلك)
القاعدة ببساطة: إذا استُدعِيَت الدالة obj.f()، فإن this هي obj أثناء استدعاء f؛ أي إما user أو admin في المثال السابق.
this == undefinedيمكننا استدعاء الدالة دون كائن:
function sayHi() {
alert(this);
}
sayHi(); // غير معرَّف
في هذه الحالة ستكون قيمة this هي undefined في الوضع الصارم. فإن حاولنا الوصول إلى this.name سيكون هناك خطأ.
في الوضع غير الصارم، فإن قيمة this في هذه الحالة ستكون المتغير العام (في المتصفح window والتي سَنشرحها في فصل المتغيرات العامة). هذا السلوك زمني يستخدم إصلاحات الوضع الصارم "use strict".
يُعد هذا الاستدعاء خطأً برمجيًا غالبًا. فإن وًجِدت this بداخل دالة، فمن المتوقع استدعاؤها من خلال كائن.
this الغير محدودة النطاقإن أتيت من لغة برمجية أخرى، فمن المتوقع أنك معتاد على “this المحدودة” إذ يمكن لِلدوال المعرَّفة في الكائن استخدام this التي ترجع للكائن.
تستخدم this بحرية في JavaScript، وتُقَيَّم قيمتها أثناء التنفيذ ولا تعتمد على المكان حيث عُرِّفت فيه، بل على الكائن الذي قبل النقطة التي استدعت الدالة.
يوجد ايجابيات وسلبيات لمبدأ تقييم this أثناء وقت التشغيل. فمن ناحية، يمكن إعادة استخدام الدالة مع عدة كائنات، ومن الناحية الأخرى، المرونة الأكثر تعطي فرصًا أكثر للخطأ. لسنا بصدد الحكم على تصميم اللغة ونعته بالجيد أم سيء، بل نحاول فهم طريقة عملها وكيفية الاستفادة من ميزاتها وتجنب الأخطاء.
لدوال السهمية لا تحوي "this
الدوال السهمية (Arrow function) هي دوال خاصة: فهي لا تملك this مخصصة لها. إن وضعنا this في إحدى هذه الدوال فَستؤخذ قيمة this من الدالة الخارجية.
مثلًا، تحصل الدالة arrow() على قيمة this من الدالة الخارجية user.sayHi():
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya
يُعد ذلك إحدى ميزات دوال الدوال السهمية، وهي مفيدة عندما لا نريد استخدام this مستقلة، ونريد أخذها من السياق الخارجي بدلًا من ذلك. سَنتعمق في موضوع الدوال السهمية لاحقًا في فصل «إعادة النظر في الدوال السهمية».
الخلاصة
- الدوال المخزنة في الكائنات تسمى «توابع» (methods).
- تسمح هذه الكائنات باستدعائها بالشكل
object.doSomething(). - يمكن للدوال الوصول إلى الكائن المعرفة فيه (أو النسخة التي استدعته المشتقة منه) باستخدام الكلمة المفتاحية
this. - تُعَرَّف قيمة
thisأثناء التنفيذ. - قد نستخدم
thisعند تعريف دالة، لكنها لا تملك أي قيمة حتى استدعاء الدالة. - يمكن نسخ دالة بين الكائنات.
- عند استدعاء دالة بالصيغة
object.method()، فإن قيمةthisأثناء الاستدعاء هيobject.
لاحظ أن الدوال السهمية مختلفة تتعامل تعاملًا مختلفًا مع this إذ لا تملك قيمة لها. عند الوصول إلى this بداخل دالة سهمية فإن قيمتها تؤخذ من النطاق الموجودة فيه.
التعليقات
<code>، وللكثير من السطور استخدم<pre>، ولأكثر من 10 سطور استخدم (plnkr, JSBin, codepen…)