Destructuring assignment این امکان را به
ES 6 اضافه کردهاست تا بتوان خواص یک شیء یا اعضای یک آرایه را با سهولت بیشتری به متغیرها نسبت داد و نگارش آن بسیار شبیه است به تعریف اشیاء یا آرایهها در جاوا اسکریپت.
Destructuring Arrays
بدون استفاده از Destructuring assignment برای دسترسی به اعضای یک آرایه و انتساب آنها به متغیرهای مختلف، روش متداول زیر مرسوم است:
var first = someArray[0];
var second = someArray[1];
var third = someArray[2];
اما با استفاده از Destructuring assignment این سه سطر، تبدیل به یک سطر میشوند:
var [first, second, third] = someArray;
همانطور که ملاحظه میکنید، سمت چپ این انتساب، بسیار شبیه است به تعریف یک آرایه، اما در اینجا مفهوم Destructuring assignment را دارد و سه متغیر جدید را تعریف میکند.
یک مثال:
let [one, two, three] = ['globin', 'ghoul', 'ghost', 'white walker'];
console.log(`one is ${one}, two is ${two}, three is ${three}`)
// => one is globin, two is ghoul, three is ghost
در اینجا ترکیبی از Destructuring assignment و
بهبودهای کار با رشتهها را در ES 6، ملاحظه میکنید. سمت چپ انتساب، سه متغیر جدید را تعریف کردهاست که این سه متغیر با سه عضو اول آرایه مقدار دهی میشوند.
همچنین در این مثال اگر علاقمند بودیم صرفا به اعضای اول و چهارم این آرایه دسترسی پیدا کنیم، میتوان نوشت:
let [firstMonster, , , fourthMonster] = ['globin', 'ghoul', 'ghost', 'white walker'];
console.log(`the first monster is ${firstMonster}, the fourth is ${fourthMonster}`)
// => one is globin, two is ghoul, three is ghost
تعریف یک کامای خالی، سبب پرش به عضو بعدی خواهد شد و به معنای صرفنظر کردن از ایندکس مطرح شدهاست. برای مثال در اینجا از ایندکسهای 2 و 3 صرفنظر شدهاست.
امکان دسترسی به اعضای تو در تو نیز با Destructuring assignment پیش بینی شدهاست:
let nested = [1, [2, 3], 4];
let [a, [b], d] = nested;
console.log(a); // 1
console.log(b); // 2
console.log(d); // 4
در مثال فوق، دومین عضو آرایه، خود نیز یک آرایهاست. برای دسترسی به این آرایهی دوم، دومین عضو Destructuring assignment نیز باید یک Destructuring assignment جدید باشد.
میتوان از Destructuring assignment جهت جابجایی مقادیر متغیرها بدون انتساب به یک متغیر موقتی نیز استفاده کرد:
let point = [1, 2];
let [xVal, yVal] = point;
[xVal, yVal] = [yVal, xVal];
console.log(xVal); // 2
console.log(yVal); // 1
در این مثال ابتدا یک آرایه با دو عضو تعریف شدهاست. سپس اعضای این آرایه به دو متغیر جدید xVal و yVal انتساب یافتهاند. در ادامه در سطر سوم، مقادیر این دو متغیر با هم تعویض شدهاند.
Destructuring Objects
امکانات Destructuring assignment، به کار با آرایهها محدود نمیشود و از آن میتوان برای کار با اشیاء نیز استفاده کرد. فرض کنید شیء pouch به صورت زیر تعریف شدهاست:
روش متداول دسترسی به خاصیت coins، به صورت pouch.coins است:
اما با استفاده از Destructuring assignment میتوان نوشت (در حالت کار با اشیاء، بجای [] از {} استفاده میشود):
در این مثال، خاصیت coins شیء pouch به متغیر جدید coins انتساب داده شدهاست. نکتهای که در اینجا باید به آن دقت داشت، همنامی متغیر جدید coins با خاصیت coins است. اگر بخواهیم این خاصیت را به یک متغیر غیرهمنام انتساب دهیم، باید به صورت زیر عمل کرد:
let pouch = {coins: 10};
let {coins: newVar1 } = pouch;
console.log(newVar1); //10
در مثال فوق، مقدار خاصیت coins به متغیر جدیدی با نام newVar1 انتساب داده شدهاست.
در اینجا نیز امکان کار با اشیای تو در تو، پیش بینی شدهاست:
let point = {
x: 1,
y: 2,
z: {
one: 3,
two: 4
}
};
let { x: a, y: b, z: { one: c, two: d } } = point;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // 4
در این مثال، خاصیت z شیء point نیز خود یک شیء دیگر است. برای دسترسی به آن همانند کار با آرایهها نیاز است از یک {} دیگر برای استخراج خواص one و two استفاده کرد.
در انتساب فوق، خاصیت x شیء point به متغیر جدید a، خاصیت y شیء point به متغیر جدید b و خاصیت one شیء منتسب به خاصیت z، به متغیر c و خاصیت two شیء منتسب به خاصیت z، به متغیر d انتساب یافتهاند.
ترکیب Destructuring Objects و Destructuring Arrays
در مثال زیر، نمونهای ترکیبی از Destructuring اشیاء و آرایهها را با هم مشاهده میکنید:
let mixed = {
one: 1,
two: 2,
values: [3, 4, 5]
};
let { one: a, two: b, values: [c, , e] } = mixed;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(e); // 5
در این مثال، خاصیت one شیء mixed به متغیر جدید a، خاصیت two آن به متغیر جدید b و اعضای اول و سوم آرایهی values به متغیرهای جدید c و e انتساب داده شدهاند. از ایندکس دوم آرایهی values نیز با معرفی یک کاما، صرفنظر گردیدهاست.
Destructuring Function Arguments
از Destructuring در حین تعریف پارامترهای متدها نیز میتوان استفاده کرد.
function removeBreakpoint({ url, line, column }) {
// ...
}
در این مثال، متد removeBreakpoint دارای سه پارامتر ورودی تعریف شدهی توسط Destructuring است. در این حالت این پارامترها به صورت خودکار از شیء ارسالی به این متد دریافت و مقدار دهی خواهند شد.
و یا برای مثال در زبان #C امکان تعریف named arguments (آرگومانهای نامدار) و همچنین تعریف مقادیر پیش فرضی برای آنها وجود دارد. در اینجا نیز میتوان با استفاده از Destructuring به تعریفی مشابه آن برای ارائهی آرگومانهایی با مقادیر پیش فرض رسید:
function random ({ min=1, max=300 }) {
return Math.floor(Math.random() * (max - min)) + min
}
console.log(random({}))
// <- 174
console.log(random({max: 24}))
// <- 18
در این مثال پارامترهای min و max تعریف شدهی با Destructuring، دارای یک مقدار پیش فرض هستند. اگر شیءایی خالی را به این متد ارسال کنیم، از مقادیر پیش فرض استفاده خواهد شد و یا اگر max را مقدار دهی کنیم، مقدار min، از مقدار پیش فرض آن دریافت میگردد.
و یا اینبار jQuery Ajax را میتوان با پارامترهای پیش فرض آن به صورت ذیل خلاصه نویسی کرد:
jQuery.ajax = function (url, {
async = true,
beforeSend = noop,
cache = true,
complete = noop,
crossDomain = false,
global = true,
// ... more config
}) {
// ... do stuff
};
همچنین اینبار امکان شبیه سازی دریافت چندین خروجی از متد، به نحو سادهتر و واضحتری میسر است:
function returnMultipleValues() {
return [1, 2];
}
var [foo, bar] = returnMultipleValues();
در ابتدا، متدی تعریف شدهاست که یک آرایهی معمولی را بازگشت میدهد. اما با استفاده از Destructuring میتوان چندین خروجی با معنا را در طی یک سطر، از آن دریافت کرد.
شبیه به همین مورد در حین کار با اشیاء نیز میسر است:
function returnMultipleValues() {
return {
foo: 1,
bar: 2
};
}
var { foo, bar } = returnMultipleValues();
متدی که یک شیء را بر میگرداند و با استفاده از Destructuring، خروجی آن به دو متغیر جدید، انتساب داده شدهاند.
تعریف مقادیر پیش فرض در حین Destructuring
در انتساب ذیل، چون شیء سمت راست، دارای خاصیت foo نیست، مقدار این پارامتر جدید undefined خواهد بود. برای رفع این مشکل میتوان به آن مقدار پیش فرضی را نیز نسبت داد:
var {foo=3} = { bar: 2 }
console.log(foo)
// <- 3
چند مثال دیگر:
اگر مقدار پیش فرض، ذکر شود و خاصیت متناظر با آن دارای مقدار باشد، از همان مقدار اصلی ذکر شده استفاده میشود:
var {foo=3} = { foo: 2 }
console.log(foo)
// <- 2
اما اگر این مقدار undefined باشد، به مقدار پیش فرض سوئیچ خواهد شد:
var {foo=3} = { foo: undefined }
console.log(foo)
// <- 3
این مورد در حین کار با آرایهها نیز برقرار است:
var [b=10] = [undefined]
console.log(b)
// <- 10
var [c=10] = []
console.log(c)
// <- 10
ES6 — default + rest + spread
علاوه بر destructuring، سه قابلیت و بهبود دیگر نیز در زمینهی کار با متغیرها و پارامترها به ES 6 اضافه شدهاند:
1) امکان تعریف مقادیر پیش فرض پارامترها function inc(number, increment) {
increment = increment || 1;
return number + increment;
}
console.log(inc(2, 2)); // 4
console.log(inc(2)); // 3
در جاوا اسکریپت، الزامی برای فراخوانی و ذکر تمام پارامترهای یک متد وجود ندارد. برای نمونه در مثال فوق میتوان متد inc را با یک و یا دو پارامتر فراخوانی کرد. در حالتیکه پارامتری ذکر نشود، مقدار آن تعریف نشده خواهد بود و روش برخورد با آن استفاده از عملگر || برای تعریف مقداری پیش فرض است. برای بهبود این وضعیت در ES 6، امکان تعریف مقدار پیش فرض پارامترها نیز درنظر گرفته شدهاست:
function inc(number, increment = 1) {
return number + increment;
}
console.log(inc(2, 2)); // 4
console.log(inc(2)); // 3
در ES 6 امکان تعریف پارامترهایی با مقادیر پیش فرض، پیش از پارامترهایی که دارای مقادیر پیش فرض نیستند نیز میسر است (برخلاف زبان سیشارپ که چنین اجازهای را نمیدهد):
function sum(a, b = 2, c) {
return a + b + c;
}
console.log(sum(1, 5, 10)); // 16 -> b === 5
console.log(sum(1, undefined, 10)); // 13 -> b as default
همچنین در حین تعریف این مقدار پیش فرض، میتوان از مقادیر غیر ثابت هم استفاده کرد (باز هم برخلاف سیشارپ). برای نمونه در مثال ذیل، خروجی یک متد، به عنوان مقدار پیش فرض پارامتری تعریف شدهاست:
function getDefaultIncrement() {
return 1;
}
function inc(number, increment = getDefaultIncrement()) {
return number + increment;
}
console.log(inc(2, 2)); // 4
console.log(inc(2)); // 3
2) Spread
متد جمع زیر را درنظر بگیرید:
function sum(a, b, c) {
return a + b + c;
}
روش متداول فراخوانی آن، ذکر تک تک آرگومانهای آن به ترتیب است. اما با استفاده از عملگر spread اضافه شده به ES 6 که با سه نقطه بیان میشود، میتوان نوشت:
var args = [1, 2, 3];
console.log(sum(…args)); // 6
عملگر spread اجازهی بسط و پخش شدن اعضای یک آرایه را به پارامترهای متناظر با آنها میدهد. به علاوه امکان ترکیب این روش، با روش متداول ذکر صریح آرگومانها نیز وجود دارد:
var args = [1, 2];
console.log(sum(…args, 3)); // 6
در این مثال، آرایهی مدنظر تنها دو عضو دارد و متد sum دارای سه پارامتر است. با استفاده از عملگر spread، دو پارامتر اول متد به صورت خودکار از آرایه واکشی شده و جایگزین میشوند. آرگومان سوم هم به صورت متداولی ذکر شدهاست.
مثالهایی از ساده سازی اعمال متداول در ES 5 (جاوا اسکریپت فعلی) با کمک ES 6:
الف) ترکیب spread و Destructuring
a = list[0], rest = list.slice(1)
معادل Destructuring ذیل است:
ب) ساده سازی کار با concat
بجای
میتوان نوشت:
ج) افزودن یک رنج به یک آرایه
بجای
list.push.apply(list, [3, 4])
میتوان نوشت:
3) Rest
جاوا اسکریپت دارای شیءایی است به نام arguments که توسط آن میتوان به لیست پارامترهای یک متد دسترسی یافت. برای نمونه مثال ذیل را درنظر بگیرید:
function sum() {
var numbers = Array.prototype.slice.call(arguments),
result = 0;
numbers.forEach(function (number) {
result += number;
});
return result;
}
در اینجا به ظاهر متد sum دارای پارامتری نیست. اما با استفاده از شیء arguments، میتوان هر تعداد آرگومانی را برای آن متصور شد و فراخوانیها ذیل کاملا مجاز هستند:
console.log(sum(1)); // 1
console.log(sum(1, 2, 3, 4, 5)); // 15
اما مشکل اینجا است که به ظاهر متد sum، هیچ پارامتری را قبول نمیکند و هدف از تعریف آن واضح نیست. برای رفع این مشکل، در ES 6 عملگر rest معرفی شدهاست که بسیار شبیه به عملگر spread است:
function sum(…numbers) {
var result = 0;
numbers.forEach(function (number) {
result += number;
});
return result;
}
console.log(sum(1)); // 1
console.log(sum(1, 2, 3, 4, 5)); // 15
در اینجا عملگر سه نقطهای rest که به عنوان پارامتر متد معرفی شدهاست، بیانگر امکان دریافت لیستی از آرگومانها، توسط متد sum است. به این ترتیب، تعریف این متد که تعداد آرگومانهای متغیری را میپذیرد، وضوح بیشتری پیدا کردهاست.
در اینجا باید دقت داشت که پس از ذکر rest، دیگر نمیتوان پارامتری را تعریف کرد:
function sum(…numbers, last) { // causes a syntax error