تُنشّأ الكائنات عادة لتُمَثِّل أشياء من العالم الحقيقي مثل المستخدمين، والطلبات، وغيرها:
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
بداخل دالة سهمية فإن قيمتها تؤخذ من النطاق الموجودة فيه.