دو ساختار داده پر استفاده در جاوااسکریپت Object و Array هستند.
- شیءها به ما این امکان را میدهند تا چیزی بسازیم که المانهای داده را به واسطه کلید ذخیره کند.
- آرایهها به ما امکان جمعآوری المانهای داده را در لیستی مرتب میدهند.
اگرچه، زمانی که ما آنها را به تابع میدهیم، ممکن است که نیازی به کل یک شیء/آرایه نباشد. شاید تنها قطعههای تکی نیاز باشد.
مقداردهیِ تجزیهکنندهی ساختار (Destructuring assignment) یک سینتکس خاص است که به ما امکان میدهد تا آرایهها یا شیءها را درون چند متغیر «پخش کنیم» چون بعضی اوقات این موضوع کار را راحتتر میکند.
تخریب ساختار همچنین با تابعهای پیچیده که تعداد زیادی پارامتر، مقدارهای پیشفرض و… دارند هم به خوبی کار میکند. به زودی آن را خواهیم دید.
تجزیه ساختار آرایه
کد پایین یک مثال از چگونگی تبدیل یک آرایه به چند متغیر است:
// ما یک آرایه شامل نام و نام خانوادگی داریم
let arr = ["John", "Smith"]
// مقداردهی تجزیهکنندهی ساختار
// را قرار میدهد firstName = arr[0]
// surname = arr[1] و
let [firstName, surname] = arr;
alert(firstName); // John
alert(surname); // Smith
حالا ما میتوانیم به جای اعداد آرایه با متغیرها کار کنیم.
زمانی که با split یا متدهای دیگری که آرایه برمیگردانند عالی بنظر میرسد:
همانطور که میبینید، سینتکس ساده است. البته چند چیز ویژه در جزییات خود دارد. بیایید برای فهمیدن بهتر آن، مثالهای بیشتری ببینیم.
این سینتکس «مقداردهی تجزیهکنندهی ساختار» نامیده میشود چون با کپی کردن المانها در چند متغیر «ساختار را تغییر میدهد». اما خود آرایه تغییر نمیکند.
فقط یک راه کوتاهتر برای نوشتن است:
// let [firstName, surname] = arr;
let firstName = arr[0];
let surname = arr[1];
المانهایی که که نمیخواهیم را میتوان با یک کامای اضافه دور انداخت:
در کد بالا، از المان دوم آرایه گذشتیم، المان سوم به title تخصیص داده شد و بقیه المانهای آرایه هم نادیده گرفته شدند (به دلیل اینکه متغیری برای ذخیره آنها وجود ندارد).
…در واقع، ما میتوانیم آن را با هر حلقهپذیری استفاده کنیم، نه فقط آرایهها:
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
این کار میکند چون از درون، یک مقداردهی تجزیهکنندهی ساختار با حلقه زدن در مقدار سمت راست کار میکند. برای فراخوانی for..of در مقدار سمت راست = و تخصیص دادن مقدارها، به نوعی خوش سینتکس است.
در فصل قبل ما متد Object.entries(obj) را دیدیم.
میتوانیم آن را با تجزیهکنندهی ساختار برای حلقه زدن در کلیدها و مقدارهای یک شیء استفاده کنیم:
کد مشابه برای Map سادهتر است چون حلقهپذیر است:
یک ترفند معروف برای مبادله مقدارهای دو متغیر با استفاده از مقداردهی تجزیهکنندهی ساختار وجود دارد:
اینجا ما یک آرایه موقتی از دو آرایه میسازیم و بلافاصله ساختار آن را نسبت به ترتیب مبادله تجزیه میکنیم.
میتوانیم از این راه بیشتر از دو متغیر را مبادله کنیم.
رِست ‘…’
معمولا، اگر آرایه طولانیتر از لیست سمت چپ باشد، المانهای «اضافی» از قلم میافتند.
برای مثال، اینجا فقط دو المان دریافت میشود و بقیه نادیده گرفته میشوند:
اگر بخواهیم تمام المانهای بعدی را دریافت کنیم – میتوانیم یک پارامتر دیگر اضافه کنیم که با استفاده از "..." «بقیه المانها» را دریافت کند:
مقدار rest آرایهای از المانهای باقی مانده است.
میتوانیم از هر اسم دیگری به جای rest برای متغیر استفاده کنیم، فقط مطمئن شوید که قبل از آن سه نقطه وجود دارد و در انتهای مقداردهی تجزیهکنندهی ساختار قرار میگیرد.
مقدارهای پیشفرض
اگر آرایه از لیست متغیرهای سمت چپ کوتاهتر باشد، هیچ اروری ایجاد نمیشود. مقدارهای ناموجود undefined در نظر گرفته میشوند:
ما یک مقدار «پیشفرض» بخواهیم که جایگزین مقدار ناموجود شود، میتوانیم با استفاده از = آن را فراهم کنیم:
مقدارهای پیشفرض میتوانند عبارات پیچیدهتر یا حتی فراخوانی تابع باشند. آنها فقط زمانی که مقدار وجود نداشته باشد ارزیابی میشوند.
برای مثال، اینجا ما از تابع prompt برای دو مقدار پیشفرض استفاده کردیم:
لطفا در نظر داشته باشید: prompt فقط برای مقدار ناموجود (surname) اجرا میشود.
تجزیه ساختار شیء
مقداردهی تجزیهکنندهی ساختار با شیءها هم کار میکند.
سینتکس ساده آن اینگونه است:
let {var1, var2} = {var1:…, var2:…}
ما باید یک شیء موجود که میخواهیم آن را در چند متغیر پخش کنیم در سمت راست داشته باشیم. سمت چپ شامل یک «الگوی» شیء مانند برای ویژگیهای متناظر میشود. در سادهترین حالت، یک لیست از اسمهای متغیر در {...} است.
برای مثال:
ویژگیهای options.title، options.width و options.height به متغیرهای متناظر تخصیص داده شدهاند.
ترتیب مهم نیست. اینگونه هم کار میکند:
// تغییر دادیم let {...} ترتیب را در
let {height, width, title} = { title: "Menu", height: 200, width: 100 }
الگوی سمت راست ممکن است پیچیدهتر باشد و رابطه بین ویژگیها و متغیرها را تعیین کند.
اگر ما بخواهیم که یک ویژگی را به یک متغیر با نامی دیگر تخصیص بدهیم، برای مثل، کاری کنیم که options.width در متغیری به نام w ذخیره شود، میتوانیم اسم متغیر یا با استفاده از دو نقطه تنظیم کنیم:
دو نقطه نشان میدهد که «چه چیزی : کجا ذخیره میشود». در مثال بالا ویژگی width درون w، ویژگی height درون h ذخیره و title به اسمی مشابه با خودش تخصیص داده میشود.
برای ویژگیهایی که ممکن است موجود نباشند ما میتوانیم با استفاده از "=" مقدار پیشفرض قرار دهیم، مثلا اینگونه:
درست مانند آرایهها یا پارامترهای تابع، مقدارهای پیشفرض میتوانند هر عبارتی یا حتی فراخوانی تابع باشند. اگر مقدار موجود نباشد، آنها ارزیابی میشوند.
در کد زیر prompt برای width درخواست میکند اما برای title نه:
همچنین ما میتوانیم دو نقطه و برابر قرار دادن را با هم ترکیب کنیم:
اگر ما یک شیء پیچیده با تعداد زیادی ویژگی داشته باشیم، میتوانیم فقط چیزی که نیاز داریم را استخراج کنیم:
الگوی رست “…”
اگر شیء بیشتر از تعدادی که ما متغیر داریم ویژگی داشته باشد چه اتفاقی میافتد؟ آیا میتوانیم بعضی از آنها را دریافت کنیم و «بقیه» را جایی دیگر ذخیره کنیم؟
میتوانیم از الگوری رست استفاده کنیم، درست همانطور که با آرایهها این کار را کردیم. این کار توسط بعضی از مرورگرهای قدیمی پشتیبانی نمیشود (IE، از Babel برای رفع این مشکل استفاده کنید) اما در مرورگرهای جدید کار میکند.
اینگونه بنظر میرسد:
let وجود نداشته باشد گرفتار میشویمدر مثالهای بالا متغیرها دقیقا درون مقداردهی تعریف شدند: let {…} = {…}. قطعا ما میتوانستیم بدون let، از متغیرهای موجود هم استفاده کنیم. اما یک مشکل وجود دارد.
این کار نمیکند:
مشکل اینجاست که جاوااسکریپت در کد اصلی (نه درون یک عبارت دیگر) با {...} به عنوان یک بلوک کد رفتار میکند. چنین بلوکهای کدی میتوانند برای گروهبندی دستورات استفاده شوند، مثلا اینگونه:
پس اینجا جاوااسکریپت فرض میکند که ما یک بلوک کد داریم و به همین دلیل است که ارور ایجاد میشود. به جای آن ما تجزیه ساختار میخواهیم.
برای اینکه به جاوااسکریپت نشان دهیم که این یک بلوک کد نیست، میتوانیم عبارت را درون پرانتز (...) قرار دهیم:
تجزیه ساختار تودرتو
اگر یک شیء یا آرایه، شیء و آرایههای تودرتو دیگری را شامل شود، ما میتوانیم از الگوری پیچیدهتری در سمت چپ برای استخراج قسمتهای عمیقتر استفاده کنیم.
در کد زیر options یک شیء دیگری درون ویژگی size و یک آرایه درون ویژگی items دارد. الگوی سمت چپ مقداردهی ساختار یکسانی برای استخراج مقدار از آنها را دارد:
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true
};
// مقداردهی تجزیهکنندهی ساختار برای واضح بودن در چند خط قرار گرفته است
let {
size: { // را اینجا قرار دهید size
width,
height
},
items: [item1, item2], // را اینجا تخصیص دهید items
title = "Menu" // در شیء وجود ندارد (مقدار پیشفرض استفاده میشود)
} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
alert(item1); // Cake
alert(item2); // Donut
تمام ویژگیهای شیء options به جز extra که در سمت چپ وجود ندارد، به متغیرهای متناظر خود تخصیص داده شدهاند:
سرانجام، ما width، height، item1، item2 و متغیر title را از مقدار پیشفرض داریم.
در نظر داشته باشید که هیچ متغیری برای size و items وجود ندارد چون به جای آنها، ما محتوایشان را میخواهیم.
پارامترهای هوشمند تابع
بعضی اوقات پیش میآید که یک تابع پارامترهای زیادی داشته باشد که اکثر آنها الزامی نیستند. خصوصا برای رابط کاربری این اتفاق میافتد. یک تابع را تصور کنید که یک منو ایجاد میکند. این منو ممکن است دارای طول(width)، ارتفاع(height)، عنوان(title)، لیستی از کالاها و غیره باشد.
این یک راه برای نوشتن چنین تابعی است:
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}
در واقعیت، مشکل این است که چگونه ترتیب آرگومانها را به یاد بسپاریم. معمولا محیطهای کدنویسی (IDE) سعی میکنند به ما کمک کنند، مخصوصا اگر کد، مستند خوبی داشته باشد اما باز هم… مشکل دیگری که وجود دارد این است که چگونه یک تابع را زمانی که اکثر پارامترها به صورت پیشفرض مشکلی ندارند فراخوانی کنیم.
مثلا اینگونه؟
// میگذاریم undefined جایی که مقدارهای پیشفرض مشکلی ندارند
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
این قشنگ نیست. و زمانی که با پارامترهای بیشتری سر و کار داشته باشیم خوانایی کمتری دارد.
تجزیه ساختار به نجات ما میآید!
ما میتوانیم پارامترها را به عنوان شیء رد کنیم و تابع بلافاصله با تجزیه ساختار، آنها را در متغیرها پخش میکند:
// ما شیء را به تابع میدهیم
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
// و تابع بلافاصله آن را در متغیرها پراکنده میکند...
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
// ،از شیء گرفته شدهاند title ،items
// ،مقدارهای پیشفرض استفاده شدند width ،height
alert( `${title} ${width} ${height}` ); // My Menu 200 100
alert( items ); // Item1, Item2
}
showMenu(options);
همچنین میتوانیم از تجزیه ساختار پیچیدهتری همراه با شیءهای تودرتو و طراحی دو نقطهای استفاده کنیم:
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
function showMenu({
title = "Untitled",
width: w = 100, // میرود w درون width
height: h = 200, // میرود h درون height
items: [item1, item2] // میرود item2 و دومی درون item1 درون items اولین المان
}) {
alert( `${title} ${w} ${h}` ); // My Menu 100 200
alert( item1 ); // Item1
alert( item2 ); // Item2
}
showMenu(options);
سینتکس کامل برای یک مقداردهی تجزیهکنندهی ساختار یکسان است:
function({
incomingProperty: varName = defaultValue
...
})
سپس به صورت پیشفرض، برای یک شیء شامل پارامترها، یک متغیر varName (اسم متغیر) برای ویژگی incomingProperty (ویژگی ورودی)، همراه با defaultValue وجود خواهد داشت.
لطفا در نظر داشته باشید که چنین تجزیه ساختاری فرض میکند که showMenu() یک آرگومان دارد. اگر ما تمام مقدارهای پیشفرض را بخواهیم، پس باید یک شیء خالی مشخص کنیم:
showMenu({}); // خب، تمام مقدارها پیشفرض هستند
showMenu(); // این ارور خواهد داد
ما میتوانیم با قرار دادن {} به عنوان مقدار پیشفرض برای تمام شیء شامل پارامترها این مشکل را رفع کنیم:
در کد بالا، تمام شیء آرگومانها به صورت پیشفرض {} است پس همیشه چیزی برای تجزیه ساختار وجود دارد.
خلاصه
-
مقداردهی تجزیهکنندهی ساختار به ما اجازه میدهد تا بلافاصله یک شیء یا آرایه را روی بسیاری از متغیرها ترسیم کنیم.
-
سینتکس کامل شیء:
let {prop : varName = default, ...rest} = objectاین به این معنی است که ویژگی
propباید درون متغیرvarNameبرود و اگر چنین ویژگیای وجود نداشت، سپس مقدارdefaultباید استفاده شود.ویژگیهای شیء که نقشی برای آنها وجود ندارد درون شیء
restکپی میشوند. -
سینتکس کامل آرایه:
let [item1 = default, item2, ...rest] = arrayالمان اول درون
item1میرود؛ المان دوم درونitem2میشود، تمام المانهای باقی مانده آرایهrestرا تشکیل میدهند. -
استخراج داده از آرایه/شیءهای تودرتو هم ممکن است، برای اینکار سمت چپ باید ساختار یکسانی با سمت راست داشته باشد.