شیءها معمولا برای نمایش چیزهایی که در دنیای واقعی هستند ساخته میشوند، مانند کاربرها، سفارشات و غیره:
let user = {
name: "John",
age: 30
};
و در دنیای واقعی، یک کاربر میتواند کاری انجام دهد برای مثال چیزی را از سبد خرید اتخاب کند، وارد سایت شود، از سایت خارج شود و غیره.
اعمال در جاوااسکریپت توسط تابعهای درون ویژگیها نمایش داده میشوند.
مثالهایی از متد
برای شروع، بیایید به user یاد بدهیم که سلام کند:
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("سلام!");
};
user.sayHi(); // !سلام
اینجا ما از Function Expression برای ساخت یک تابع استفاده کردیم و آن را به ویژگی user.sayHi شیء تخصیص دادیم.
سپس میتوانیم آن را با user.sayHi() صدا بزنیم. حالا user میتواند صحبت کند!
تابعی که یک ویژگی از شیءای باشد متد آن نامیده میشود.
پس اینجا ما یک متد sayHi از شیء user داریم.
قطعا ما میتوانستیم از تابعی که قبلا تعریف شده است استفاده کنیم، مثل اینجا:
let user = {
// ...
};
// اول تعریف میکنیم
function sayHi() {
alert("سلام!");
};
// سپس به عنوان متد آن را اضافه میکنیم
user.sayHi = sayHi;
user.sayHi(); // !سلام
زمانی که ما با استفاده از شیء برای نمایش چیزهای موجود کد مینویسیم، به آن برنامهنویسی شیءگرا میگویند، یا به طور خلاصه: “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() { // یکسان است "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); // "this" به جای "user"
}
};
…اما چنین کدی قابل اطمینان نیست. اگر ما تصمیم بگیریم که 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 استفاده میکردیم، کد کار میکرد.
“this” محدود نیست
در جاوااسکریپت، کلمه کلیدی this متفاوت از بیشتر زبانهای برنامهنویسی دیگر رفتار میکند. این کلمه میتواند در هر تابعی استفاده شود، حتی اگر آن تابع متدی از یک شیء نباشد.
در مثال پایین هیچ سینتکس اروری وجود ندارد:
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;
// متقاوتی دارند this این صدازدنها
// درون تابع همان شیء "قبل نقطه" است "this"
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (نقطه یا براکتها به متد دسترسی دارند - مسئلهی مهمی نیست)
قاعده ساده است: اگر obj.f() صدا زده شود، سپس در حین صدازدن f، this برابر با obj است. پس در مثال بالا یا برابر با user است یا admin.
this == undefinedما حتی میتوانیم تابع را بدون هیچ شیءای صدا بزنیم:
function sayHi() {
alert(this);
}
sayHi(); // undefined
در این مورد this در حالت سختگیرانه (strict mode) برابر با undefined است. اگر ما تلاش کنیم که به this.name دسترسی پیدا کنیم، یک ارور به وجود میآید.
در حالت غیر سختگیرانه در چنین موردی مقدار this برابر است با global object (در مرورگر window است، ما در فصل شیء گلوبال به سراغ آن میرویم). این یک رفتار تاریخی است که "use strict" آن را درست میکند.
معمولا چنین صدازدنی یک ارور برنامهنویسی است. اگر this درون یک تابع باشد، انتظار میرود که در زمینهی شیء صدا زده شود.
this بدون محدودیتاگر شما از یک زبان برنامهنویسی دیگری میایید، پس شما احتمالا به نظریه “this محدود” عادت کردهاید، که متدهای تعریفشده درون یک شیء همیشه دارای یک this هستند که به همان شیء رجوع میکند.
در جاوااسکریپت this “آزاد” است، مقدار آن هنگام صدا زدن ارزیابی میشود و به اینکه متد کجا تعریف شده بستگی ندارد و بلکه به اینکه شیء “قبل از نقطه” چه باشد بستگی دارد.
اینکه this هنگام اجراشدن ارزیابی میشود فواید و زیانهایی دارد. از طرفی، یک تابع میتواند برای شیءهای متفاوت استفاده شود. از طرفی دیگر، هر چقدر اعطاف بیشتر باشد احتمالات برای اشتباهات هم بیشتر میشود.
اینجا ما به دنبال این نیستیم که درباره خوب یا بد بودن طراحی این زبان قضاوت کنیم. ما چگونه کارکردن با آن، چگونه سود بردن از آن و چگونگی جلوگیری از مشکلات را یاد میگیریم.
تابعهای Arrow “this” ندارند
Arrow functionها خاص هستند: آنها از “خودشان” this ندارند. اگر ما از this در چنین تابعی استفاده کنیم، مقدار آن از تابع “معمولی” بیرونی گرفته میشود.
برای مثال، اینجا arrow() از this متد بیرونی user.sayHi() استفاده میکند:
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya
این یک ویژگی خاص arrow functionها است، و زمانی که ما نمیخواهیم یک this جداگانه داشته باشیم بلکه آن را از محتوای بالاتر بگیریم، از آن استفاده میکنیم. بعدا در فصل سرکشی دوباره از تابعهای کمانی ما در arrow functionها عمیقتر میشویم.
خلاصه
- تابعهایی که در ویژگیهای شیءها ذخیره میشوند “متد” نامیده میشوند.
- متدها به شیءها اجازه میدهند که “کاری انجام دهند” مثل
object.doSomething(). - متدها میتوانند با
thisبه شیء رجوع کنند.
مقدار this هنگام اجرا تعریف میشود.
- هنگامی که یک تابع تعریف میشود، ممکن است از
thisاستفاده کند، اما آنthisتا زمانی که تابع صدا زده نشود مقداری ندارد. - یک تابع میتواند بین شیءها کپی شود.
- زمانی که یک تابع با سینتکس “متد” صدا زده میشود:
object.method()، مقدارthisدر حین صدازدن برابر باobjectاست.
لطفا در نظر داشته باشید که arrow functionها خاص هستند: آنها this ندارند. زمانی که به this درون یک arrow function دسترسی پیدا میکنیم، مقدار آن از بیرون تابع گرفته میشود.
نظرات
<code>استفاده کنید، برای چندین خط – کد را درون تگ<pre>قرار دهید، برای بیش از ده خط کد – از یک جعبهٔ شنی استفاده کنید. (plnkr، jsbin، codepen…)