شیءها معمولا برای نمایش چیزهایی که در دنیای واقعی هستند ساخته میشوند، مانند کاربرها، سفارشات و غیره:
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 دسترسی پیدا میکنیم، مقدار آن از بیرون تابع گرفته میشود.