آموزش جاوا اسکریپت – جلسه ۷ – آبجکت [Object]
![آموزش جاوا اسکریپت – جلسه 7 – آبجکت [Object]](https://rahkarino.ir/wp-content/uploads/2020/05/js-part7-object.jpg)
سلام دوستان همراه راهکاری نو!
در بخش قبل یعنی فصل 6 از آموزش جاوااسکریپت نحوه کدنویسی استاندارد و صحیح را در جاوا اسکریپت بیان کردیم. که شامل نحوه درست کامنت نویسی، استفاده صحیح از semi-colon و {} و indent ، ابزارهای لینتر در جاوا اسکریپت و… بود.
در این مقاله سعی داریم تمام مباحث مربوط به آبجکت یا شیء را در زبان جاوا اسکریپت آموزش دهیم.
همانطور که در فصل “انواع داده” یا Data-Types ذکر شد، ما 8 نوع داده در جاوااسکریپت داریم. 7 تای آنها به “primitive” معروف هستند. زیرا فقط یک مقدار می توانند داشته باشند (مانند رشته یا عدد).
در حالیکه اشیاء (Objects) می توانند چندین نوع داده را در خود جای دهند. نوع داده شی در جاوا اسکریپت تقریبا در تمام جنبه های این زبان برنامه نویسی نفوذ کرده است. پس توصیه می شود آن را بخوبی درک کنید.
تعریف آبجکت، دسترسی به آبجکت و حذف و اضافه کردن آیتم در آن:
در این بخش دستورات مرتبط با نحوه دستیابی به یک object ، حذف یک آیتم از آبجکت و درج یک آیتم جدید در آن را معرفی خواهیم کرد.
نحوه تعریف یک آبجکت جدید در جاوا اسکریپت:
یک آبجکت با علامت {} تعریف می شود. درون آن مقادیر ورودی با فرمت “key: value” تعریف خواهند شد. آیتم key از نوع رشته است و آیتم value از هر نوعی می تواند باشد.
یک آبجکت خالی می تواند به یکی از دو روش زیر تعریف شود:
let user = new Object(); // "object constructor" syntax let user = {}; // "object literal" syntax
ساختار دوم که بنام object literal معروف است روش رایج تری است.
می توان در حین اینکه آبجکت را تعریف می کنیم برای آن مقدار هم نسبت دهیم:
let user = { // an object name: "John", // by key "name" store value "John" age: 30 // by key "age" store value 30 };
یک property در آبجکت شامل دو بخش است: key که به name یا identifier هم می گویند سپس علامت : در نهایت value.
دسترسی به مقدار یک آیتم از آبجکت:
برای دسترسی به آیتم های یک آبجکت بصورت زیر عمل می کنیم:
// get property values of the object: alert( user.name ); // John alert( user.age ); // 30
افزودن یک آیتم به آبجکت:
در دستور زیر یک آیتم جدید بنام isAdmin با نوع بولین به آبجکت user اضافه کرده ایم که مقدارش true است:
user.isAdmin = true;
حذف یک آیتم از آبجکت:
برای حذف یک آیتم از آبجکت داریم:
delete user.age;
قوانین نام گذاری در آبجکت های جاوا اسکریپتی:
در این بخش، مباحث مربوط به نام گذاری key و property در آبجکت های جاوا اسکریپتی و همچنین نام گذاری و اعلان صحیح خود آبجکت را بیان می کنیم.
تعریف نام چند کلمهای برای نام key:
همچنین می توانید نام یا key یک آیتم در آبجکت را بصورت چند بخشی تعریف کنید (مثلا like birds) اما باید در دابل کوتیشن بنویسید:
let user = { name: "John", age: 30, "likes birds": true // multiword property name must be quoted };
نکته: در این حالت (نام چند کلمه ای) نمی توان با دات (.) به مقدار آن دسترسی داشت:
// this would give a syntax error user.likes birds = true
در مواردی که key چند کلمه ایست می توان برای دسترسی به مقدار آن در آبجکت یا حذف آن می توان از براکت ([]) استفاده کرد:
let user = {}; // set user["likes birds"] = true; // get alert(user["likes birds"]); // true // delete delete user["likes birds"];
علاوه بر این می توان عبارت چند کلمه ای مورد نظر را ابتدا در یک متغیر (مثلا key) تعریف کرد سپس از آن در آبجکت استفاده کرد:
let key = "likes birds"; // same as user["likes birds"] = true; user[key] = true;
تعریف ویرگول در انتهای آیتم های آبجکت:
در ضمن می توان در انتهای لیست آیتم های یک آبجکت از علامت ویرگول استفاده کنید.
let user = { name: "John", age: 30, }
به این ویرگول trailing یا hanging می گویند. در اینصورت افزودن یا حذف یک آیتم از مقادیر آبجکت ساده تر خواهد بود.
در مثال زیر از یک تابع built-in جاوا اسکریپت بنام prompt برای پرسش از کاربر استفاده کرده ایم:
let user = { name: "John", age: 30 }; let key = prompt("What do you want to know about the user?", "name"); // access by variable alert( user[key] ); // John (if enter "name")
در کد بالا ابتدا یک سوال از کاربر پرسیده می شود که می خواهد چه اطلاعاتی از کاربر را بداند؟ اگر کاربر عبارت name را وارد کند در مرحله بعدی با یک alert روبرو می شود که کلمه john در آن نوشته شده است و اگر age را در prompt بنویسد با عدد 30 روبرو خواهد شد.
نمی توان از دات (.) بصورت زیر برای دسترسی به مقدار آبجکت استفاده کرد:
let user = { name: "John", age: 30 }; let key = "name"; alert( user.key ) // undefined
تعریف نام کلید در آبجکت بصورت داینامیک (Computed Property):
می توان از براکت در object literal ها استفاده کرد. به آنها computed property می گویند.
let fruit = prompt("Which fruit to buy?", "apple"); let bag = { [fruit]: 5, // the name of the property is taken from the variable fruit }; alert( bag.apple ); // 5 if fruit="apple"
در مثال بالا نام property آبجکت از متغیر fruit گرفته شده است که fruit نیز بصورت سوال از کاربر پرسیده می شود. یعنی هر مقداری که کاربر وارد کند بعنوان نام property آبجکت در نظر گرفته می شود. در این مثال اگر کاربر کلمه apple را وارد کند عدد 5 نمایش خواهد یافت.
مثال زیر همان کار کد بالا را می کند (اما زیباتر و تمیزتر است!):
let fruit = prompt("Which fruit to buy?", "apple"); let bag = {}; // take property name from the fruit variable bag[fruit] = 5;
ضمنا می توان مقدار پیچیده تری داخل براکت تعریف کرد:
let fruit = 'apple'; let bag = { [fruit + 'Computers']: 5 // bag.appleComputers = 5 };
براکت ها نسبت به دات (.) قابلیت بیشتری در اختیار برنامه نویس قرار می دهند. در حالت کلی اگر نام property ساده و یک بخشی باشد استفاده از دات بلامانع است اما اگر نامی پیچیده تر یا محاسباتی دارد باید از براکت استفاده شود.
هم نام بودن متغیرها و property های آبجکت:
در نمونه کد واقعی، معمولا نام property های آبجکت با نام متغیرهای موجود یکسان در نظر گرفته می شود. بعنوان مثال:
function makeUser(name, age) { return { name: name, age: age, // ...other properties }; } let user = makeUser("John", 30); alert(user.name); // John
در کد بالا نام متغیرهای name و age با آیتم های key در آبجکت user یکسان هستند.
به این دلیل که نام ها یکسان است می توان مثلا بجای اینکه بنویسیم name: name فقط بنویسیم name
function makeUser(name, age) { return { name, // same as name: name age, // same as age: age // ... }; }
محدودیت های نام گذاری property های آبجکت:
- نوع داده نام property: یا کلید هر آیتم از اشیا در جاوا اسکریپت باید بصورت رشته (string) باشد. اگر از نوع دیگری تعریف شود (مثلا عدد 0) اتوماتیک به رشته تبدیل خواهد شد:
let obj = { 0: "test" // same as "0": "test" }; // both alerts access the same property (the number 0 is converted to string "0") alert( obj["0"] ); // test alert( obj[0] ); // test (same property)
- تعریف کلمات رزرو شده جاوااسکریپت بعنوان نام key: همانطور که اطلاع دارید، نمی توان از کلمات کلیدی رزرو شده زبان های برنامه نویسی برای نام گذاری متغیرها استفاده کرد اما در انتخاب اسامی برای property های آبجکت این محدودیت وجود ندارد و می توان از کلماتی مانند for و let و … استفاده کرد:
let obj = { for: 1, let: 2, return: 3 }; alert( obj.for + obj.let + obj.return ); // 6
- استثناء در نام گذاری: در جاوا اسکریپت اگر از کلمه __proto__ برای نام کلید آبجکت استفاده کنید با یک نتیجه غیر قابل پیش بینی مواجه خواهید شد.
let obj = {}; obj.__proto__ = 5; // assign a number alert(obj.__proto__); // [object Object] - the value is an object, didn't work as intended
در مثال بالا احتمالا انتظار دارید در خروجی عدد 5 را مشاهده کنید اما با عبارت [object object] مواجه خواهید شد!
دستورات بررسی key و property در آبجکت:
در این بخش، به بررسی و جستجوی یک key یا property خاص در آبجکت های جاوا اسکریپتی خواهیم پرداخت.
بررسی وجود property در آبجکت توسط کلمه کلیدی in:
برای اینکه چک کنیم آیا یک نام property در object موردنظر مان وجود دارد یا خیر می توان از in با فرمت زیر استفاده کرد:
"key" in object
مثال زیر:
let user = { name: "John", age: 30 }; alert( "age" in user ); // true, user.age exists alert( "blabla" in user ); // false, user.blabla doesn't exist
دقت کنید که در این ساختار باید سمت چپ in یک رشته باشد و سمت راست آن نام آبجکت موردنظر.
اگر در سمت چپ in از نوع داده ای غیر از رشته استفاده شود جاوا اسکریپت اینگونه برداشت می کند که متغیری برای آن نام property تعریف کرده اید:
let user = { age: 30 }; let key = "age"; alert( key in user ); // true, takes the name from key and checks for such property
بررسی تمام کلیدهای آبجکت توسط حلقه for..in:
به منظور لیست کردن و بررسی تمام کلیدهای یک آبجکت در جاوا اسکریپت، می توان از ساختار for..in استفاده کرد. دقت کنید این ساختار با حلقه for(;;) که قبلا داشتیم کاملا متفاوت است.
for (key in object) { // executes the body for each key among object properties }
در مثال زیر تمام کلیدهای آبجکت یوزر را چاپ کرده ایم:
let user = { name: "John", age: 30, isAdmin: true }; for (let key in user) { // keys alert( key ); // name, age, isAdmin // values for the keys alert( user[key] ); // John, 30, true }
توجه کنید که نام key در کد بالا دلخواه است اما مرسوم است که یا key تعریف شود یا prop. در مثال بالا در حلقه for..in تمام آیتم های key: value مربوط به آبجکت user را به شکل alert نمایش می دهیم.
ترتیب نمایش آیتم های آبجکت:
اگر نام key در آبجکت بصورت عددی باشد به ترتیب کوچکترین عدد به بزرگترین عدد آیتم ها را نمایش می دهد:
let codes = { "49": "Germany", "41": "Switzerland", "44": "Great Britain", // .., "1": "USA" }; for (let code in codes) { alert(code); // 1, 41, 44, 49 }
مثال بالا ابتدا USA با کد 1 سپس Switzerland با کد 41 سپس Britain با کد 44 و در نهایت Germany با کد 49 نمایش می یابد. اما اگر در این مثال بخواهیم آیتم های آبجکت codes به ترتیبی که تعریف شده اند اجرا شوند، می توان از یک علامت + در ابتدای نام هریک از آیتم ها استفاده کرد. به این دلیل که به رشته تبدیل شوند.
let codes = { "+49": "Germany", "+41": "Switzerland", "+44": "Great Britain", // .., "+1": "USA" }; for (let code in codes) { alert( +code ); // 49, 41, 44, 1 }
اما اگر نام های property از نوع string باشند به ترتیب تعریف هر آیتم نمایش می یابند.
let user = { name: "John", surname: "Smith" }; user.age = 25; // add one more // non-integer properties are listed in the creation order for (let prop in user) { alert( prop ); // name, surname, age }
یکی از تفاوت های اساسی بین آبجکت ها و primitive type ها اینست که اشیاء بصورت reference کپی و ذخیره می شوند. بعنوان مثال در کد زیر، هر دو متغیر message و phrase شامل کلمه Hello هستند:
let message = "Hello!"; let phrase = message;
اما آبجکت ها بدین صورت نمی باشند.
یک متغیر خود آبجکت را ذخیره نمی کند بلکه آدرس آن را در حافظه (memory) ذخیره می کند. بعبارتی دیگر یک reference به آن را در حافظه نگهداری می کند.
let user = { name: "John" };
وقتی یک متغیر آبجکت کپی می شود خود آبجکت کپی (duplicate) نمی شود بلکه آدرس محل ذخیره آن در مموری (reference) کپی می شود.
اگر ما به آبجکت بعنوان یک کابینت نگاه کنیم variable بعنوان کلید آن کابینت خواهد بود. کپی کردن یک متغیر کلید کابینت را کپی می کند نه خود کابینت را !
let user = { name: "John" }; let admin = user; // copy the reference
در واقع ما دو متغیر داریم که هر دوی آنها به آبجکت user رفرنس داده اند.
ما می توانیم از هریک از این متغیرها برای دستیابی به آبجکت و دستکاری آن استفاده کنیم:
let user = { name: 'John' }; let admin = user; admin.name = 'Pete'; // changed by the "admin" reference alert(user.name); // 'Pete', changes are seen from the "user" reference
در کد بالا می توان گفت ما فقط یک کابینت بنام user داریم که دو کلید به نام های user و admin برای آن ساخته شده است.
مقایسه آبجکت ها در جاوا اسکریپت:
عملگرهای تساوی که قبلا گفتیم، یعنی == و === در اینجا هم کاربرد دارند.
دو آبجکت مساوی هستند فقط اگر یک آبجکت باشند. بعنوان مثال اگر دو متغیر به یک آبجکت واحد رفرنس بدهند، مساوی خواهند بود:
let a = {}; let b = a; // copy the reference alert( a == b ); // true, both variables reference the same object alert( a === b ); // true
اما اگر حتی دو آبجکت متفاوت خالی باشند مساوی نیستند:
let a = {}; let b = {}; // two independent objects alert( a == b ); // false
تعریف آبجکت از نوع const:
آبجکت هایی که به شکل زیر از نوع ثابت const تعریف شوند قابل تغییر و دستکاری هستند:
const user = { name: "John" }; user.age = 25; // (*) alert(user.age); // 25
شاید در نگاه اول بنظر بیاید که خط * خطا دهد اما در عمل این دستور بدون مشکل اجرا می شود و هیچ اروری ندارد. دلیلش اینست که متغیر user که رفرنس آبجکت را درون خود دارد بصورت const تعریف شده است و نه خود آبجکت. پس مقادیر آبجکت قابل تغییر هستند.
اما اگر سعی کنیم متغیر user را مجددا تعریف کنیم و برابر آبجکت دیگری قرار دهیم (reassign) با ارور مواجه می شویم:
const user = { name: "John" }; // Error (can't reassign user) user = { name: "Pete" };
متدهای Clone و Merge و Object.assign:
بنابراین کپی کردن یک آبجکت باعث می شود یک رفرنس دیگر به آن آبجکت ایجاد شود. اما اگر بخواهیم یک آبجکت را duplicate کنیم و یک کپی مستقل از آن ایجاد کنیم باید چه کرد؟
انجام اینکار شدنی است اما نیاز به کد بیشتری دارد. زیرا بطور پیش فرض انجام اینکار در جاوا اسکریپت تعریف نشده است. اگرچه کم پیش میاید که نیاز به ایجاد یک کپی مستقل از یک آبجکت داشته باشیم و اکثر مواقع با ایجاد رفرنس جدید به شی کارمان راه میوفتد.
اما اگر به هر دلیلی می خواهیم یک کپی مستقل از آبجکت ایجاد کنیم و آن را در واقع duplicate کنیم، باید یک آبجکت جدید تعریف کنیم و structure کدهای آبجکت قبلی را در آبجکت جدید کپی کنیم. به شکل زیر:
let user = { name: "John", age: 30 }; let clone = {}; // the new empty object // let's copy all user properties into it for (let key in user) { clone[key] = user[key]; } // now clone is a fully independent clone clone.name = "Pete"; // changed the data in it alert( user.name ); // still John in the original object
همچنین می توان از دستور Object.assign استفاده کرد:
Object.assign(dest, [src1, src2, src3...])
مثالی از ساختار فوق:
let user = { name: "John" }; let permissions1 = { canView: true }; let permissions2 = { canEdit: true }; // copies all properties from permissions1 and permissions2 into user Object.assign(user, permissions1, permissions2); // now user = { name: "John", canView: true, canEdit: true }
اگر آبجکت گیرنده یعنی user شامل همان property باشد، overwrite خواهد شد.
let user = { name: "John" }; // overwrite name, add isAdmin Object.assign(user, { name: "Pete", isAdmin: true }); // now user = { name: "Pete", isAdmin: true }
همچنین می توان بوسیله object.assign یک clone ساده انجام داد:
let user = { name: "John", age: 30 }; let clone = Object.assign({}, user);
کد بالا تمام property های آبجکت user را در یک آبجکت خالی کپی می کند.
نوع Symbol:
همانطور که در فصل آبجکت ها در جاوااسکریپت گفتیم، کلید property در آبجکت فقط می تواند یا از نوع رشته (string) باشد یا از نوع سمبل (symbol type). پس کلید یک آبجکت نمی تواند عدد، بولین و… باشد.
تا الان فقط به نوع رشته در property key های آبجکت پرداختیم. در این بخش می خواهیم نوع symbol را معرفی کنیم.
یک “symbol” بیانگر یک مقدار یکتا (unique) است که توسط تابع Symbol() تولید می شود.
// id is a new symbol let id = Symbol();
هنگام ایجاد یک symbol می توان برای آن یک توضیح یا description تعریف کرد. (غالبا در خطایابی یا debugging کاربرد دارد)
// id is a symbol with the description "id" let id = Symbol("id");
جاوا اسکریپت گارانتی کرده که مقادیری که symbol تولید می کند یکتا و منحصر بفرد باشد. یعنی اگر شما تعداد بسیار زیادی از این نوع داده را تعریف کنید، هیچکدام از آنها یکسان نخواهد بود.
نکته: دقت کنید که یکسان بودن مقدار description تاثیری در مقدار سمبل ندارد. بعنوان مثال در کد زیر رو symbol با توضیحات یکسان تعریف کرده ایم اما با هم برابر نیستند:
let id1 = Symbol("id"); let id2 = Symbol("id"); alert(id1 == id2); // false
نوع داده symbol بطور اتوماتیک به string تبدیل نمی شود:
در جاوا اسکریپت اغلب انواع داده ها می توانند به نوع رشته تبدیل شوند اما Symbol استثنا است. در تابع پیش فرض alert می توان تقریبا هر نوع داده ای تعریف کنید. جاوا اسکریپت بطور اتوماتیک آن را به نوع رشته تبدیل می کند اما نوع داده symbol قابل تبدیل به رشته نمی باشد. بعنوان مثال کد زیر خطا دارد:
let id = Symbol("id"); alert(id); // TypeError: Cannot convert a Symbol value to a string
به این دلیل که دو نوع رشته و سمبل اساسا متفاوت هستند و نمی توانند به یکدیگر تبدیل شوند.
اما اگر اصرار داریم مقدار symbol را در alert نمایش دهیم، باید آن را ابتدا توسط toString به رشته تبدیل کرده سپس نمایش دهیم:
let id = Symbol("id"); alert(id.toString()); // Symbol(id), now it works
همچنین بصورت زیر می توان description سمبل را نمایش داد:
let id = Symbol("id"); alert(id.description); // id
استفاده از کلمه کلیدی this و تعریف متد در آبجکت:
تعریف متد در آبجکت:
آبجکت ها معمولا برای پیاده سازی یک موجودیت در دنیای واقعی تعریف می شوند. مانند: order و user
let user = { name: "John", age: 30 };
در دنیای واقعی مثلا یک کاربر می تواند اکشن و رفتارهایی داشته باشد: مانند افزودن یک محصول به سبد خرید، لاگین به فروشگاه و…
اکشن ها در جاوا اسکریپت توسط توابع در property های آبجکت تعریف می شوند.
مثال هایی از متد:
برای شروع بیایید فعالیت “سلام کردن” را برای یوزر تعریف کنیم:
let user = { name: "John", age: 30 }; user.sayHi = function() { alert("Hello!"); }; user.sayHi(); // Hello!
در قطعه کد بالا، یک اکشن بنام sayHi برای یوزر تعریف کردیم که بصورت function expression پیاده شده است. در دستور آخر نیز آن را فراخوانی کرده ایم.
تابعی که بعنوان یکی از propertyهای آبجکت تعریف می کنیم متد (Method) نام دارد.
نکته: اگر تابع مورد نظر از قبل نوشته شده باشد می توان آن را به شکل زیر بعنوان متد آبجکت تعریف کرد:
let user = { // ... }; // first, declare function sayHi() { alert("Hello!"); }; // then add as a method user.sayHi = sayHi; user.sayHi(); // Hello!
برنامه نویسی شی گرا (Object Oriented):
وقتی ما در اسکریپت خود از اشیاء برای پیاده سازی موجودیت ها (Entity) استفاده می کنیم به آن برنامه نویسی شی گرا (OOP) گویند.
شی گرایی یک مبحث علمی مفصل است که موارد زیادی را در بر می گیرد: مثلا چگونه یک موجودیت صحیح را انتخاب کنیم؟ چگونه آن را پیاده سازی کنیم؟ نحوه ارتباط آن با سایر موجودیت ها به چه صورت است؟ و…
تعریف خلاصه متد در آبجکت:
به روش زیر می توان توابع آبجکت (یعنی متد) را خلاصه تر نوشت:
// these objects do the same user = { sayHi: function() { alert("Hello"); } }; // method shorthand looks better, right? user = { sayHi() { // same as "sayHi: function()" alert("Hello"); } };
استفاده از کلمه کلیدی this در متد:
طبیعی است که در داخل بدنه متدهای آبجکت به سایر property های آن آبجکت نیاز پیدا کنیم. بعنوان مثال در متد sayHi از آبجکت یوزر به نام کاربر نیاز داریم. برای دسترسی به property های آبجکت از داخل method از کلمه رزرو شده this استفاده می کنیم.
برای مثال:
let user = { name: "John", age: 30, sayHi() { // "this" is the "current object" alert(this.name); } }; user.sayHi(); // John
بطور تکنیکی می توان به روشی بغیر از this به property مورد نظر دسترسی داشت:
let user = { name: "John", age: 30, sayHi() { alert(user.name); // "user" instead of "this" } };
اما این مدل کدنویسی غیرقابل اعتماد است. زیرا ممکن است بعدا در برنامه user را به متغیر دیگری منتقل کنیم (admin = user) و سپس متغیر user را به نحو دیگری تعریف کنیم (overwrite). در اینصورت برنامه با خطا مواجه خواهد شد.
مثال زیر:
let user = { name: "John", age: 30, sayHi() { alert( user.name ); // leads to an error } }; let admin = user; user = null; // overwrite to make things obvious admin.sayHi(); // Whoops! inside sayHi(), the old name is used! error!
در کد بالا اگر (داخل alert) بجای user.name بنویسیم this.name ، ارور برنامه رفع می شود.
رفتار this و نحوه استفاده از آن در جاوا اسکریپت با سایر زبان های برنامه نویسی کمی متفاوت است. از کلمه کلیدی this در هر تابعی می توان استفاده کرد. کد زیر هیچ خطای دستوری ندارد:
function sayHi() { alert( this.name ); }
مقدار this در زمان run-time ارزیابی می شود.
در مثال زیر از this در دو آبجکت متفاوت استفاده شده است:
let user = { name: "John" }; let admin = { name: "Admin" }; function sayHi() { alert( this.name ); } // use the same function in two objects user.f = sayHi; admin.f = sayHi; // these calls have different this // "this" inside the function is the object "before the dot" user.f(); // John (this == user) admin.f(); // Admin (this == admin) admin['f'](); // Admin (dot or square brackets access the method – doesn't matter)
خروجی های alert به ازای فراخوانی user.f و admin.f متفاوت است.
نکته: اگر کلمه this را بدون آبجکت تعریف کنیم، (در strict mode) خروجی undefined خواهد شد. مثال زیر:
function sayHi() { alert(this); } sayHi(); // undefined
چنانچه در مد strict نباشیم، خروجی مثال فوق [object Window] خواهد بود.
نکته: در یک متد پیچیده، استفاده اشتباه از کلمه this می تواند باعث بروز خطا شود.
let user = { name: "John", hi() { alert(this.name); }, bye() { alert("Bye"); } }; user.hi(); // John (the simple call works) // now let's call user.hi or user.bye depending on the name (user.name == "John" ? user.hi : user.bye)(); // Error!
در دستور آخر، می خواهیم بر اساس مقدار name در آبجکت user یکی از دو متد hi یا bye را فراخوانی کنیم. یعنی اگر name برابر john بود متد hi وگرنه bye اجرا شود. در انتهای این خط به منظور اجرای آن پرانتز باز و بسته تعریف شده است که باعث بروز خطا می شود. زیرا کلمه this در اینحالت برابر undefined خواهد شد.
اما اگر متدها را به شکل user.hi(); تعریف کنیم بدرستی اجرا خواهند شد.
Arrow Function ها کلمه this ندارند:
توابع arrow در جاوا اسکریپت استثنا هستند. کلمه this ندارند. اگر در یک تابع arrow از this برای رفرنس دادن به یک متغیر یا تابع استفاده شود، مقدارش را از اولین تابع خارجی نرمال می گیرد:
let user = { firstName: "Ilya", sayHi() { let arrow = () => alert(this.firstName); arrow(); } }; user.sayHi(); // Ilya
در مثال بالا، از کلمه this در یک arrow function استفاده شده است. پس this به اولین تابع یا آبجکت نرمال والد آن یعنی user اشاره می کند. مقدار this.firstName یعنی user.firstName
متد سازنده (constructor) و کلمه new:
بطور معمول از علامت {…} برای تعریف یک آبجکت استفاده می شود. اما در اغلب موارد ما نیاز داریم چندین شی شبیه هم در برنامه خود تعریف کنیم مانند یوزرهای مختلف یا آیتم های منو و…
توسط توابع سازنده (constructor) و کلمه کلیدی new میتوان اینکار را انجام داد.
متد سازنده (Constructor Method):
از لحاظ تکنیکی توابع constructor همانند توابع عادی می باشد اما از دو لحاظ متفاوت هستند:
- نام این توابع با حروف بزرگ تعریف می شوند (Capital Letter)
- باید حتما با کلمه new فراخوانی شوند.
مثال:
function User(name) { this.name = name; this.isAdmin = false; } let user = new User("Jack"); alert(user.name); // Jack alert(user.isAdmin); // false
وقتی یک فانکشن توسط کلمه new اجرا می شود، موارد زیر انجام می شوند:
- یک آبجکت خالی ایجاد می شود و داخل this ریخته می شود.
- بدنه تابع اجرا می شود و مقدار داخل this آپدیت شده و property های جدید در آن درج می شود.
- مقدار داخل this بازگردانده می شود.
به بیان دیگر، User(…) کاری شبیه کد زیر را انجام می دهد:
function User(name) { // this = {}; (implicitly) // add properties to this this.name = name; this.isAdmin = false; // return this; (implicitly) }
بنابراین نتیجه دستور let user = new User(“Jack”) با کد زیر یکسان است:
let user = { name: "Jack", isAdmin: false };
الان اگر بخواهیم یوزرهای جدید بسازیم، new User(“Ann”), new User(“Alice”) را خواهیم داشت.
اگر می خواهیم در کد خود چندین آبجکت مجزا تعریف کنیم می توان بصورت زیر در تابع سازنده آنها را تعریف کرد:
let user = new function() { this.name = "John"; this.isAdmin = false; // ...other code for user creation // maybe complex logic and statements // local variables etc };
حالت تست سازنده (new.target):
در بدنه تابع اگر بخواهیم بفهمیم آن تابع بوسیله کلمه new ایجاد شده یا خیر، می تواند از new.target به شکل زیر استفاده کرد:
function User() { alert(new.target); } // without "new": User(); // undefined // with "new": new User(); // function User { ... }
در قطعه کد فوق اگر تابع توسط new فراخوانی شده باشد خروجی خود تابع خواهد بود و اگر تابع به شکل عادی فراخوانی شده باشد خروجی new.target برابر undefined خواهد شد.
مقدار بازگشتی از تابع سازنده:
در حالت عادی توابع سازنده دستور return ندارند. وظیفه آنها تعریف تمام مقادیر در this است. اما اگر لازم بود return تعریف شود، قانون آن در سازنده ها ساده است:
- اگر return با آبجکت فراخوانی شود، خروجی بجای this آبجکت خواهد بود.
- اگر return با یک نوع primitive صدا زده شود، دستور return صرف نظر می شود.
به بیان دیگر اگر return در توابع سازنده با آبجکت صدا زده شود، خروجی همان آبجکت است و اگر با هر نوع داده دیگر اجرا شود، خروجی همان this خواهد بود.
بعنوان مثال در کد زیر return بجای this آبجکت را برمی گرداند.
function BigUser() { this.name = "John"; return { name: "Godzilla" }; // <-- returns this object } alert( new BigUser().name ); // Godzilla, got that object
در زیر هم یک مثال از return خالی بیان شده است:
function SmallUser() { this.name = "John"; return; // <-- returns this } alert( new SmallUser().name ); // John
نکته 1: غالبا متد سازنده دستور return ندارد. ما در بخش بالا فقط موارد استثناء را بیان کردیم.
نکته 2: اگر تابع سازنده هیچ آرگومانی نداشته باشد، می توان پرانتز آن را حذف کرد:
let user = new User; // <-- no parentheses // same as let user = new User();
متدها در توابع سازنده:
استفاده از سازنده ها باعث می شود انعطاف پذیری بیشتری در تعریف توابع داشته باشیم. همانطور که می توان در سازنده property تعریف کرد، می توانیم متد هم داشته باشیم. بعنوان مثال در قطعه کد زیر برای آبجکت User یک property بنام name تعریف شده و یک متد بنام sayHi
function User(name) { this.name = name; this.sayHi = function() { alert( "My name is: " + this.name ); }; } let john = new User("John"); john.sayHi(); // My name is: John /* john = { name: "John", sayHi: function() { ... } } */
در این مقاله (فصل هفتم از آموزش جامع جاوا اسکریپت) سعی کردیم تمام مباحث کاربردی مربوط به اشیاء (objects) را در جاوا اسکریپت آموزش دهیم.
لطفا اگر سوال یا پیشنهادی درباره این آموزش دارید از طریق فرم ارسال دیدگاه با ما و سایر کاربران “راهکارینو” در میان بگذارید.
در مقاله بعد یعنی فصل 8 از آموزش javascript با آموزش انواع داده یا Data-Types (مانند عدد و رشته و…) در جاوااسکریپت در خدمت شما خواهیم بود.
دیدگاهتان را بنویسید