جاوا اسکریپت در سالهای گذشته خیلی پیشرفت کرده است. اگر سال 2017 جاوا اسکریپت یاد گرفتهاید و هنوز اصطلاح ES6 به گوش شما ناآشناست و با آن کار نکردهاید، باید گفت، یک راه راحت و سریع برای خواندن و نوشتن کدهای جاوا اسکریپت را از دست دادهاید. اگر در جاوا اسکریپت حرفهای نیستید، نگران نباشید. برای بهره بردن از فیچرهای ES6 نیازی نیست که جاوا اسکریپت را خیلی حرفهای بلد باشید. در این مقاله قصد داریم تا با 8 فیچر اکما اسکریپت 6 آشنا شویم که روزانه مورد استفاده دولوپرها قرار میگیرند.
ابتدا به خاطر داشته باشید که اکمااسکریپت 6، آپدیتی بزرگ برای جاوا اسکریپت است.
اجازه ندهید این اصطلاحات و لیست طولانی شما رو بترساند و از یاد گرفتن ES6 منصرفتان کند. قرار نیست بلافاصله همه چیز رو کامل و دقیق یاد بگیرید. ما در کنار شما هستیم تا 8 مورد از این لیست رو با هم بررسی کنیم.
خب اول برویم سراغ var که اصطلاح نام آشناتری است. اول از همه میتوانیم متغیرها را با کلید واژه var
مشخص کنیم. بعد از این کار این کلمه کلیدی میتواند در هر بخشی از اسکوپ جاری مورد استفاده قرار بگیرد. (scope محدودهای است که متغییر در آن قابل استفاده است، برای مثال اگر متغییر داخل یک فانکشن تعریف شود، محدوده استفاده از آن همان فانکشن است و خارج از فانکشن تعریف نشده است.)
در قطعه کد بالا me به عنوان متغیر گلوبال تعریف شده است. با توجه به تعریفی که از var داشتم، متغیر گلوبال me میتواند در در یک فانکشن یا تابع به شکل زیر مورد استفاده قرار بگیرد.
با این حال عکس این موضوع امکان پذیر نیست.یعنی ما نمیتوانیم یک متغیر را در یک فانکشن تعریف کنیم و بیرون از فانکشن از آن استفاده کنیم.
اگر متغیر خارج از فانکشن ساخته شده باشد، در اسکوپ خارجیتری وجود خواهد دارد.
خب اولین سوالی که در این قسمت باید برای شما پیش بیاید این است که بلاک چیست و چرا با رسم شکل؟!!
یک بلاک در جاوا اسکریپت تمام آن چیزی است که در براکت گنجانده شده است. کدی که در ادامه ملاحظه میکنید شامل مثالهایی از بلاک است.
تفاوت بین متغیرهای block-scope و function-scoped زیاد است. وقتی از یک متغیر function-scoped استفاده میکنید، ممکن است به طور تصادفی متغیر را بدون قصد انجام این کار بازنویسی کنید. یک مثال داریم:
در این مثال مشاهده میکنید که me به Sleepy head تبدیل شده است. این مثال مشکلی برای شما ایجاد نخواهد کرد زیرا احتمالاً شما متغیرهایی را با همین نام دوباره تعریف نمیکنید.
اما ممکن است کسی که با var در یک حلقه for کار کند، در رابطه با حوزه و محدوده متغیرها دچار کمی سردرگمی و ابهام شود.
تصویر زیر خروجی قطعه کد بالا است.
داخل پرانتز این توضیح کوتاه را داشته باشید که این روش closure نامیده میشود. در واقع کلوژر، فانکشنی است که به اسکوپ یا محدوده فانکشن خارجی خود حتی بعد از ریترن شدن این فانکشن خارجی دسترسی دارد.
مثال timeout که بالاتر نوشته بودیم را میتوان به شکل زیر هم نوشت. این قطعه کد به درستی کار میکند و نیازی به نوشته هیچ فانکشن اضافهای هم نیست.
i
به درستی اجرا شده است. 1 و 2 و 3 و 4
همانطور که مشاهده میکنید متغیرهای block-scoped با حذف مشکلات پیشبینی نشده متداول متغییرهای function-scoped، کدنویسی را خیلی راحت کردهاند. در نهایت برای اینکه از کدنویسی لذت بیشتری ببرید، پیشنهاد میکنیم از این به بعد هر جا که نیاز داشتید کد جاوا اسکریپت بزنید، از let
بالای var
استفاده کنید.
حالا که میدانیم let
چه کاری انجام میدهد، اجازه دهید به تفاوت بین let
و const
بپردازیم.
Let در مقابل const
const
هم مانند let
یک blocked-scoped هست. تفاوت در این است که مقادیر const
بعد از تعریف، قابل تغییر و تعریف مجدد نیست.
const name = 'Zell'
name = 'Sleepy head' // TypeError: Assignment to constant variable.
let name1 = 'Zell'
name1 = 'Sleepy head'
console.log(name1) // 'Sleepy head'
این ویژگی const
برای متغیرهایی خوب است که قرار نیست مقادیر آنها تغییر کند. در واقع به این اسامی که به خانهای از حافظه اشاره دارند و حاوی یک مقدار هستند، اگر قابل تغییر باشند متغییر و اگر قابل تغییر نباشند، ثابت میگویند. در اینجا name
یک ثابت و name1
یک متغییر است.
یک مثال ملموستر بزنیم. فرض کنید دکمهای در وب سایت ما وجود دارد که قرار است یک مدال را باز کند. میدانیم که این دکمه همیشه یک دکمه است و هیچ تغییری نخواهد داشت. بنابراین بهترین روش استفاده کردن از const
است.
const modalLauncher = document.querySelector('.jsModalLauncher')
در حالت کلی هر جایی که متغیر تعریف میکنیم، ترجیحمان این است که در حد ممکن از const
به جای let
استفاده کنیم. چون پیغام "نمیتوان مقدار متغیرها را تغییر داد" دریافت میکنیم. برای تمامی موقعیتهای دیگر از let
استفاده میکنیم.
فانکشن arrow
فانکشن arrow با نماد (=>
) مشخص میشود. این فانکشن، کدنویسی برای فانکشنهای بی نام (anonymous functions) را کوتاه میکند. در واقع میتوان گفت باعث مختصرنویسی میشود. هر جایی که کلمه کلیدی function استفاده شده باشد میتوان از آنها استفاده کرد. مانند مثال زیر :
let array = [1,7,98,5,4,2]
// ES5 way
var moreThan20 = array.filter(function (num) {
return num > 20
})
// ES6 way
let moreThan20 = array.filter(num => num > 20)
فانکشن arrow خیلی محبوب است. نظر شما را به چند مزیت این فانکشن جلب میکنیم. 1- این فانکشن کدها را کوتاهتر میکند. مسلما کوتاهتر شدن کدها یعنی کمتر شدن خطا و سریعتر شدن فرآیند کدنویسی. 2- این فانکشن به شما کمک میکنند تا کدی بنویسید که درک آن راحتتر باشد. برویم سراغ سروکله زدن با Arrow Functions تا کامل آنها را یاد بگیریم.
مفاهیم مربوط arrow functions
برای شروع کار بهتر است درباره ساخت فانکشنها صحبت کنیم. در جاوا اسکریپت، احتمالا فانکشنها را به شکل زیر تعریف میکنید.
function namedFunction() {
// Do something
}
// using the function
namedFunction()
روش دومی هم برای تعریف توابع وجود دارد. میتوانید یک فانکشن بدون نام ایجاد کنید و آن را به یک متغیر اختصاص بدهید. برای ساختن فانکشن بدون نام، اسم فانکشن را بیرون اعلان فانکشن تعریف میکنیم.
var namedFunction = function() {
// Do something
}
روش سومی نیز وجود دارد، که تعریف کردن مستقیم آنها به عنوان یک آرگومنت در فانکشن یا متدی دیگر است. این روش سوم برای تعریف فانکشنهای بی نام خیلی روش مرسوم و متداولی است. یک مثال هم داریم:
// Using an anonymous function in a callback
button.addEventListener('click', function() {
// Do something
})
از آن جایی که فانکشنهای arrow در اکمااسکریپت 6، شکل کوتاه شده فانکشنهای بی نام هستند، همیشه میتوانید فانکشنهای arrow را جایگزین فانکشنهای بی نام کنید.
// Normal Function
const namedFunction = function (arg1, arg2) { /* do your stuff */}
// Arrow Function
const namedFunction2 = (arg1, arg2) => {/* do your stuff */}
// Normal function in a callback
button.addEventListener('click', function () {
// Do something
})
// Arrow function in a callback
button.addEventListener('click', () => {
// Do something
})
در این مثال ساده شدن کد کاملا قابل مشاهده است. کلمه کلیدی function را حذف میکنید و کمی آن طرفتر =>
را جایگزین آن میکنید. اما کارهای بزرگتر دیگری هم میتوانیم با فانکشن arrow انجام بدهیم. فقط قرار نیست function را با =>
جایگزین کنیم. نحوه عملکرد این فانکشن با دو فاکتور میتواند تغییر کند:
تعداد آرگومنتهای ضروری
اینکه آیا میخواهید یک ایمپلیسیت ریترن (implicit return) داشته باشید.
فاکتور اول تعداد آرگومنت های داده شده به فانکشن است. اگر فقط یک آرگومنت را داده باشید، میتوانید پرانتزهای آرگومنت را بردارید. حالا اگر هیچ آرگومنتی نیاز نداشته باشید چه؟!! در این صورت میتوانید پرانتزها را با آندراسکور (_
) جدا کنید. چند خط کدی که در ادامه میبینید در فانکشن arrow معتبر است.
const zeroArgs = () => {/* do something */}
const zeroWithUnderscore = _ => {/* do something */}
const oneArg = arg1 => {/* do something */}
const oneArgWithParenthesis = (arg1) => {/* do something */}
const manyArgs = (arg1, arg2) => {/* do something */}
و اما فاکتور دوم. اگر کد فقط یک خط را بگیرد ، فانکشنهای arrow به صورت خودکار کلمه کلیدی return را ایجاد میکنند و در یک بلاک قرار نمیگیرد . بنابراین این دو خط معادل هستند:
const sum1 = (num1, num2) => num1 + num2
const sum2 = (num1, num2) => { return num1 + num2 }
اینکه میگوییم میتوانید کدهای کوتاهتری بنویسید، همین دو فاکتور هستند.
let array = [1,7,98,5,4,2]
// ES5 way
var moreThan20 = array.filter(function (num) {
return num > 20
})
// ES6 way
let moreThan20 = array.filter(num => num > 20)
در ادامه باید یکی دیگر از ویژگی فانکشن arrow یعنی the lexical this را توضیح دهیم.
The lexical this
this یک کلمه کلیدی منحصر به فرد است که مقدار آن بستگی به این دارد که کجا و به چه صورت فراخوانی شود. اگر خارج از فانکشن فراخوانی شود، به صورت پیشفرض به آبجکت Window در مرورگر اشاره میکند.
console.log(this) // Window
به صورت پیشفرض به آبجکت Window در مرورگر اشاره میکند.
اگر this
در یک فانکشن ساده فراخوانی شود، در یک گلوبال آبجکت ست میشود. در مورد مرورگرها this
همیشه Window میشود.
function hello () {
console.log(this)
}
hello() // Window
جاوا اسکریپت همیشه داخل یک فانکشن ساده، this را برای آبجکتwindow ست میکند. زمانی که this به عنوان یک object method فراخوانی میشود، خودش یک آبجکت محسوب میشود.
let o = {
sayThis: function() {
console.log(this)
}
}
o.sayThis() // o
این به آبجکت اشاره دارد، هنگامی که فانکشن در یک آبجکت متد فراخوانی شده است.
زمانی که تابع به عنوان یک constructor فراخوانی می شود، this
به به یک آبجکت تازه ساخته شده اشاره میکند.
function Person (age) {
this.age = age
}
let greg = new Person(22)
let thomas = new Person(24)
console.log(greg) // this.age = 22
console.log(thomas) // this.age = 24
این آبجکت به constructed object فراخوانی شده با کلمه کلیدی new
یا Object.create()
اشاره دارد
زمانی که در یک event listener فراخوانی شود، this
به المانی که فایر شده اشاره میکند. (اونت لسنر یا EventListener یک فانکشن است که منتظر میماند تا اتفاقی رخ دهد تا عملی را در واکنش به آن اتفاق انجام دهد.)
let button = document.querySelector('button')
button.addEventListener('click', function() {
console.log(this) // button
})
همانطور که در مثالهای بالا مشاهده کردید، مقدار this
توسط فانکشنی که آن را فراخوانی میکند ست میشود. هر فانکشن مقدار this مربوط به خودش را تعریف میکند. در فانکشنهای fat arrow، مهم نیست که this
به چه صورت فراخوانی میشود، this به مقدار جدید محدود نمیشود. مقدار this
همیشه برابر با مقدار کدهای اطراف آن خواهد بود. (به هر حال، lexical به معنی مرتبط با است که میتوانیم حدس بزنیم چرا lexical this این نام را گرفته). صد البته که توضیحات کمی گیج کننده بود، پس بهترین راه بررسی با مثال است.
اول از همه اینکه شما هرگز نمیخواهید از arrow functions برای اعلام object methods استفاده کنید ، چون دیگر نمیتوانید آبجکت را به this
ارجاع دهید.
let o = {
// Don't do this
notThis: () => {
console.log(this) // Window
this.objectThis() // Uncaught TypeError: this.objectThis is not a function
},
// Do this
objectThis: function () {
console.log(this) // o
}
// Or this, which is a new shorthand
objectThis2 () {
console.log(this) // o
}
}
دوم اینکه ممکن است نخواهید، برای ایجاد کردن event listeners از arrow functions استفاده کنید چون this
به عنصری که شما به event listener متصل کردهاید، وصل نمیشود. به هر حال همیشه میتوانید this
را با event.currentTarget دریافت کنید.
button.addEventListener('click', function () {
console.log(this) // button
})
button.addEventListener('click', e => {
console.log(this) // Window
console.log(event.currentTarget) // button
})
سوم اینکه ممکن است شما بخواهید از کلمات در جاهایی استفاده کنید که this
بدون اینکه شما بخواهید آنها را تغییر دهد. مثال تابع timeout را در ادامه داریم که شما مجبور نیستید با this یا that یا self سروکله بزنید.
let o = {
// Old way
oldDoSthAfterThree: function () {
let that = this
setTimeout(function () {
console.log(this) // Window
console.log(that) // o
})
},
// Arrow function way
doSthAfterThree: function () {
setTimeout(() => {
console.log(this) // o
}, 3000)
}
}
این مورد در مواقعی که شما به اضافه یا حذف کردن یک کلاس بعد از گذشت مدت زمانی نیاز دارید، مفید است.
let o = {
button: document.querySelector('button')
endAnimation: function () {
this.button.classList.add('is-closing')
setTimeout(() => {
this.button.classList.remove('is-closing')
this.button.classList.remove('is-open')
}, 3000)
}
}
در نهایت از fat arrow function استفاده کنید تا کدهای شما کوتاه و کوتاهتر شود. مانند مثال moreThan20 که بالاتر داشتیم.
let array = [1,7,98,5,4,2]
let moreThan20 = array.filter(num => num > 20)
پارامترهای پیشفرض
Default parameters راهی برای تعیین پارامترهای پیش فرض هنگام تعریف فانکشنها میدهند. برویم سراغ یک مثال تا ببینید این آیتم چقد مفید است.فانکشنی ایجاد میکنیم که نام یک بازیکن از یک تیم را اعلام کند.
اگر شما این تابع را در ES5 تعریف کنید، شبیه قطعه کد پایین خواهد بود:
function announcePlayer (firstName, lastName, teamName) {
console.log(firstName + ' ' + lastName + ', ' + teamName)
}
announcePlayer('Stephen', 'Curry', 'Golden State Warriors')
// Stephen Curry, Golden State Warriors
در نگاه اول این کد اوکی هست. ولی اگر ما مجبور باشیم بازیکنی که در هیچ تیمی نباشد چطور؟ اگر teamName را مقداردهی نکنیم، کد فعلی با شکست مواجه میشود
announcePlayer('Zell', 'Liew')
// Zell Liew, undefined
من کاملا مطمئنم که undefined
، یک تیم نیست. :)
اگر بازیکن وابسته به هیچ تیمی نباشد، اعلام Zell Liew, unaffiliated بسیار منطقیتر از Zell Liew, undefined است. موافقید؟
برای اینکه فانکشن Zell Liew, unaffiliated را اعلام کند، تنها راه، پس کردن استرینگ unaffiliated به عنوان teamName است:
announcePlayer('Zell', 'Liew', 'unaffiliated')
// Zell Liew, unaffiliated
اگر چه این کد درست کار میکند. با ریفکتور کردن unaffiliated در announcePlayer به وسیله چک کردن اینکه آیا teamName تعریف شده است، میتوانیم کد بهتری بنویسیم. (ریفکتور یا refactor به معنی تغییر ساختار کد بدون تغییر عملکرد آن است. در واقع کد ریفکتور شده همان خروجی سابق را دارد، منتها تکمیل یا بهبود یافته). در ورژن ES5 میتوانید کد را به شکل زیر ریفکتور کنید:
function announcePlayer (firstName, lastName, teamName) {
if (!teamName) {
teamName = 'unaffiliated'
}
console.log(firstName + ' ' + lastName + ', ' + teamName)
}
announcePlayer('Zell', 'Liew')
// Zell Liew, unaffiliated
announcePlayer('Stephen', 'Curry', 'Golden State Warriors')
// Stephen Curry, Golden State Warriors
یا اگر میخواهید با استفاده از ترنری اپراتورها (ternary operators) کد کمتری بنویسید، میتوانید فانکشن را به صورت زیر تعریف کنید:
function announcePlayer (firstName, lastName, teamName) {
var team = teamName ? teamName : 'unaffiliated'
console.log(firstName + ' ' + lastName + ', ' + team)
}
در اکما اسکریپت 6 با پارامترهای پیشفرض میتوانیم هر جایی که خواستیم پارامتر تعریف کنیم، یک علامت مساوی =
اضافه کنیم. اگر ما این کار را انجام دهیم وقتی پارامتری تعریف نشده باشد ES6 به صورت پیشفرض آن را مقداردهی میکند. بنابراین طبق کدهای زیر وقتی که teamName تعریف نشده باشد، به صورت پیشفرض مقدار unaffiliated برای آن در نظر گرفته میشود.
const announcePlayer = (firstName, lastName, teamName = 'unaffiliated') => {
console.log(firstName + ' ' + lastName + ', ' + teamName)
}
announcePlayer('Zell', 'Liew')
// Zell Liew, unaffiliated
announcePlayer('Stephen', 'Curry', 'Golden State Warriors')
// Stephen Curry, Golden State Warriors
یک مورد دیگر اینکه اگر میخواهید مقدار پیشفرض را فراخوانی کنید، میتوانید به صورت دستی مقدار undefined
را به آن پس کنید. پس کردن دستی undefined
هنگامی که پارامتر پیشفرض شما آخرین آرگومان نیست، به شما کمک میکند.
announcePlayer('Zell', 'Liew', undefined)
// Zell Liew, unaffiliated
این تمام چیزی بود که شما باید درباره default parameters میدانستید. خیلی مختصر و مفید.
Destructuring
Destructuring یک روش راحت برای خروجی گرفتن مقادیر از آرایه ها و آبجکتها است. بین آبجکتها و آرایههای destructuring تفاوتهای جزئی وجود دارد، بنابراین اجازه دهید جداگانه درباره آنها صحبت کنیم.
آبجکتهای ساختارشکنانه (Destructuring objects)
با فرض اینکه شما آبجکت زیر را دارید:
const Zell = {
firstName: 'Zell',
lastName: 'Liew'
}
برای گرفتن firstName
و lastName
از Zell، شما باید 2 متغیر بسازید بعد مقدار هر متغیر را به خودش اختصاص دهید. شبیه قطعه کد زیر:
let firstName = Zell.firstName // Zell
let lastName = Zell.lastName // Liew
با Destructuring میتوانید این کار را در یک خط کد انجام دهید.
let { firstName, lastName } = Zell
console.log(firstName) // Zell
console.log(lastName) // Liew
با اضافه کردن براکت ها ({}
) زمانی که متغیرها را تعریف میکنیم، به جاوا اسکریپت میگوییم که متغیرهای مذکور را ایجاد کند. سپس Zell.firstName را به firstName
و Zell.lastName را به lastName
اختصاص دهد.
// What you write
let { firstName, lastName } = Zell
// ES6 does this automatically
let firstName = Zell.firstName
let lastName = Zell.lastName
حالا اگر نام یک متغیر استفاده شده باشد، نمیتوانیم دوباره متغیر را دوباره اعلام کنیم. به ویژه اگر از دستور let
یا const
استفاده کرده باشیم. قطعه کد زیر با شکست مواجه میشود:
let name = 'Zell Liew'
let course = {
name: 'JS Fundamentals for Frontend Developers'
// ... other properties
}
let { name } = course // Uncaught SyntaxError: Identifier 'name' has already been declared
اگر شما در شرایطی شبیه بالا قرار گرفتید، میتوانید هنگام destructuring، متغیرها را با استفاده از کولن (:
) تغییر نام دهید. در مثال زیر یک متغیر با عنوان courseName ایجاد شده است و course.name به آن اختصاص داده شده است.
let { name: courseName } = course
console.log(courseName) // JS Fundamentals for Frontend Developers
// What ES6 does under the hood:
let courseName = course.name
نکته
اگر یک متغیر را درون یک شی که وجود ندارد destructure کردید نگران نباشید. undefined
را بر میگرداند.
let course = {
name: 'JS Fundamentals for Frontend Developers'
}
let { package } = course
console.log(package) // undefined
یک نکته جالب دیگر اینکه شما میتوانید default parameters را برای متغیرهای destructured خود بنویسید. سینتکس آن شبیه تعریف کردن توابع است.
let course = {
name: 'JS Fundamentals for Frontend Developers'
}
let { package = 'full course' } = course
console.log(package) // full course
حتی میتوانید متغیرها را هنگام تهیه پیشفرضها تغییر نام دهید. فقط این دو را ترکیب کنید.
let course = {
name: 'JS Fundamentals for Frontend Developers'
}
let { package: packageName = 'full course' } = course
console.log(packageName) // full course
آرایههای ساختارشکنانه (Destructuring arrays)
Destructuring arrays و destructuring objects شبیه به هم هستند. با این تفاوت که از ([]
) به جای ({}
) استفاده میکنیم. وقتی یک آرایه را destructure میکنید:
اولین متغییر، اولین آیتم در آرایه است.
دومین متغییر، دومین آیتم در آرایه است.
و .....
let [one, two] = [1, 2, 3, 4, 5]
console.log(one) // 1
console.log(two) // 2
اگر تعداد متغییرها بیشتر از تعداد آیتمها باشد میتوان آنها را از بین برد. اگر چنین شرایطی پیش بیاید، مقدار undefined
برگردانده میشود.
let [one, two, three] = [1, 2]
console.log(one) // 1
console.log(two) // 2
console.log(three) // undefined
هنگام destructuring arrays ما فقط متغیرهای مورد نیاز خود را destructure میکنیم. در صورت نیاز به بقیه آرایه، میتوانید از اپراتور (...) استفاده کنید ، مانند این:
let scores = ['98', '95', '93', '90', '87', '85']
let [first, second, third, ...rest] = scores
console.log(first) // 98
console.log(second) // 95
console.log(third) // 93
console.log(rest) // [90, 87, 85]
درباره اپراتور rest در سکشن زیر بیشتر صحبت خواهیم کرد.
تعویض متغیرها با destructured arrays
فرض کنید شما دو متغیر دارید. a و b
let a = 2
let b = 3
میخواهید مقادیر این دو متغیر را جابجا کنید. یعنی a = 3
و b = 2
در ES5 شما به یک متغیر سومی نیاز دارید تا این جابجایی را انجام دهید.
let a = 2
let b = 3
let temp
// swapping
temp = a // temp is now 2
a = b // a is now 3
b = temp // b is now 2
درست است که این قطعه کد صحیح عمل میکند ولی خب ممکن کمی گیج کننده باشد. حالا ببینید در ES6 با استفاده از destructured arrays چطور این کار را انجام میدهیم.
let a = 2
let b = 3; // semicolon required because next line begins with a square bracket
// Swapping with destructured arrays
[a, b] = [b, a]
console.log(a) // 3
console.log(b) // 2
خیلی راحت تر از روش قبلی :)) حالا برویم سراغ destructuring arrays و objects در یک تابع
Destructuring arrays and objects هنگام تعریف فانکشنها
یک ویژگی جالب destructuring این است که میتوان از آنها هر جایی استفاده کرد حتی در توابع. تابعی داریم که آرایهای از امتیازها را میگیرد و یک آبجکت با سه امتیاز بالا برمیگرداند. این تابع مشابه کاری است که ما در هنگام destructuring arrays انجام دادهایم.
// Note: You don't need arrow functions to use any other ES6 features
function topThree (scores) {
let [first, second, third] = scores
return {
first: first,
second: second,
third: third
}
}
یک روش جایگزین برای نوشتن این فانکشن، destructure کردن scores هنگام تعریف فانکشن است. در این حالت، یک خط کد کمتر برای نوشتن وجود دارد. در عین حال، میدانیم که آرایهای را در دست داریم.
function topThree ([first, second, third]) {
return {
first: first,
second: second,
third: third
}
}
یک کوئیز کوچک سریع برای شما داریم. هنگام تعریف فانکشن، میتوانیم پارامترهای پیشفرض و destructure را ترکیب کنیم، کد زیر چه میگوید؟
function sayMyName ({
firstName = 'Zell',
lastName = 'Liew'
} = {}) {
console.log(firstName + ' ' + lastName)
}
در این بخش چند ویژگی را با هم ترکیب کردهایم.
میبینیم که این فانکشن یک آرگومنت میپذیرد که یک آبجکت است. این آبجکت اختیاری است و در صورت عدم تعریف، مقدار پیشفرض آن {} است.
سعی میکنیم firstName
و lastName
را از آبجکت داده شده destructure کنیم. اگر این پراپرتیها یافت شوند، از آنها استفاده میکنیم.
اگر firstName
و یا lastName
تعریف نشده باشند، به ترتیب Zell و Liew قرار داده میشوند.
بنابراین این تابع نتایج زیر را تولید میکند:
sayMyName() // Zell Liew
sayMyName({firstName: 'Zell'}) // Zell Liew
sayMyName({firstName: 'Vincy', lastName: 'Zhang'}) // Vincy Zhang
پارامتر rest و اپراتور spread
این دو پارامتر و اپراتور شبیه به هم هستند. هر دو با سه نقطه (...) نشان داده میشوند. اینکه این دو مورد چه کاری انجام میدهند، بستگی به این دارد که برای چه چیزی استفاده میشوند. بیایید کمی دقیقتر این دو پارامترو اپراتور را بررسی کنیم.
پارامتر rest
این پارامتر بخش مشخصی از آرگومنتها را میگیرد و در یک آرایه دیگر قرار میدهد. در واقع لیست آرگومانهایی که با کاما (,) جدا شدهاند را به یک آرایه تبدیل میکند.
بیایید در عمل نگاهی به عملکرد پارامتر rest بیاندازیم. فرض کنید تابع add را داریم که آرگومنتها را جمع میکند.
sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // 55
در ES5، هنگامی که با فانکشنی سروکار داشتیم که تعداد نامشخصی متغییر میپذیرفت، باید از متغییر arguments استفاده میکردیم.
متغییر arguments یک سیمبل شبه آرایه (array-like) است.
function sum () {
console.log(arguments)
}
sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
arguments یک آرایه نیست بلکه Symbol است.
یکی از راههای محاسبه کردن مجموع این آرگومنتها، تبدیل کردن آن به آرایه با استفاده از Array.prototype.slice.call(arguments)
و سپس سپس استفاده از حلقه forEach
یا reduce
است. مسلما حلقه forEach
را خودتان میتوانید بنویسید. به همین علت در اینجا مثال reduce
را داریم.
// ES5 way
function sum () {
let argsArray = Array.prototype.slice.call(arguments)
return argsArray.reduce(function(sum, current) {
return sum + current
}, 0)
}
با پارامتر rest در ES6 میتوانیم تمام آرگومانهای جدا شده از کاما را مستقیما درون یک آرایه بچسبانیم.
// ES6 way
const sum = (...args) => args.reduce((sum, current) => sum + current, 0)
// ES6 way if we didn't shortcut it with so many arrow functions
function sum (...args) {
return args.reduce((sum, current) => sum + current, 0)
}
کدها تمیزتر نشدند؟!!
در ادامه ما یک آرایه از امتیازها را به سه امتیاز بالا destructure کردیم.
let scores = ['98', '95', '93', '90', '87', '85']
let [first, second, third] = scores
console.log(first) // 98
console.log(second) // 95
console.log(third) // 93
اگر ما بخواهیم از دستور rest استفاده کنیم. میتوانیم اینکار را با پارامترهای rest در یک آرایه انجام دهیم.
let scores = ['98', '95', '93', '90', '87', '85']
let [first, second, third, ...restOfScores] = scores
console.log(restOfScores) // [90, 87, 85]
اگر توضیحات گیج کننده بود، این موضوع را به یاد داشته باشید که پارامتر rest همه چیز را در یک آرایه پک و بستهبندی میکند. آن در پارامترهای تابع و زمانی که آرایهها destructuring می شوند، ظاهر میشود.
برویم سراغ اپراتور بعدی.
اپراتور spread
این اپراتور برعکس پارامتر rest کار میکند. یک آرایه را میگیرد و آن را به لیستی از آرگومنتهای جداشده با کاما گسترش میدهد (شبیه فشردگی)
let array = ['one', 'two', 'three']
// These two are exactly the same
console.log(...array) // one two three
console.log('one', 'two', 'three') // one two three
این اپراتور اغلب اوقات برای راحتتر و خواناتر کردن آرایهها مورد استفاده قرار میگیرد.
برای مثال شما میخواهید آرایههای زیر را concatenate یا به هم وصل کنید.
let array1 = ['one', 'two']
let array2 = ['three', 'four']
let array3 = ['five', 'six']
راهی که در ES5 پیشنهاد میشد این بود که دو آرایه از متد Array.concat
استفاده کنند. شما با این متد میتوانید هر تعداد آرایهای را به هم متصل کنید.
// ES5 way
let combinedArray = array1.concat(array2).concat(array3)
console.log(combinedArray) // ['one', 'two', 'three', 'four', 'five', 'six']
در ES6 اپراتور spread وارد عمل میشود. میتوانید آرایهها را در یک آرایه جدید منتشر کنید. شبیه به مثال زیر که خواندن آن هم بسیار راحتتر است.
// ES6 way
let combinedArray = [...array1, ...array2, ...array3]
console.log(combinedArray) // ['one', 'two', 'three', 'four', 'five', 'six']
این اپراتور یک کارایی دیگر هم دارد و آن اینکه میتوانید یک آیتم را از یک آرایه حذف کنید. بدون اینکه نیازی به mutating کردن آرایه داشته باشید. این روش معمولا در ریداکس (Redux) استفاده میشود.
Enhanced object literals
آبجکتها برای شما باید یک مفهوم نام آشنا باشند. چون با جاوا اسکریپت کدنویسی کردهاید. فقط تنها چیزی که میتوان گفت شما راجع به آن اطلاعاتی ندارید، چیزی شبیه به قطعه کد پایین است.
const anObject = {
property1: 'value1',
property2: 'value2',
property3: 'value3',
}
ES6 enhanced object literals سه آیتم ارتقا و بهبود را در آبجکتهایی که میشناسید، به ارمغان آورده است.
Property value shorthands
Method shorthands
The ability to use computed property names
هر کدام را جداگانه با هم بررسی میکنیم. قول میدهیم که چیزهایی یاد خواهید گرفت که باعث سرعت عمل شما شود.
شکل کوتاه Property value
حتما برای شما پیش آمده که مقداری را به یک متغیر اختصاص داده باشید که همنام با آبجکت پراپرتی باشد. چیزی شبیه به قطعه کد زیر:
const fullName = 'Zell Liew'
const Zell = {
fullName: fullName
}
خب با توجه به اینکه پراپرتی و متغییر حاوی مقدار همنام هستند، دوست نداشتید این کار را به روش سادهتری انجام میدادید؟
خبر خوب اینکه میتوانید :)
اگر نام متغیر و نام پراپرتی یکی است، میتوانید فقط متغیر را بنویسید. چیزی شبیه به قطعه زیر
const fullName = 'Zell Liew'
// ES6 way
const Zell = {
fullName
}
// Underneath the hood, ES6 does this:
const Zell = {
fullName: fullName
}
زیبا نیست؟ تعداد کلمات کمتری داریم و خیلی لذتبخش بود.
شکل کوتاه Method
متدها فانکشنهایی هستند که با یک پراپرتی همراه هستند. در ادامه یک مثال از متد داریم:
const anObject = {
aMethod: function () { console.log("I'm a method!~~")}
}
در ES6 میخواهیم متدها را خلاصه نویسی کنیم. میتوانیم function را از اعلان متد حذف کنیم و خواهیم دید که مانند قبل به درستی کار میکند.
const anObject = {
// ES6 way
aShorthandMethod (arg1, arg2) {},
// ES5 way
aLonghandMethod: function (arg1, arg2) {},
}
با استفاده از این بروزرسانی، آبجکتها شکل کوتاه shorthand را دریافت میکنند. بنابراین لطفا هنگام تعریف آبجکتها از arrow functions استفاده نکنید. زیرا این چارچوب را درهم میشکنید. (پیشنهاد میکنیم کمی بالاتر مفاهیم arrow functions را مجددا مطالعه کنید.)
const dontDoThis = {
// Noooo. Don't do this
arrowFunction: () => {}
}
Computed object property names
ممکن است هنگام تعریف یک آبجکت به یک نام پراپرتی داینامیک نیاز داشته باشید. در روش قدیمی جاوا اسکریپت، باید یک آبجکت ایجاد میکردید، بعد property خود را به آن اختصاص میدادید.
// ES5
const newPropertyName = 'smile'
// Create an object first
const anObject = { aProperty: 'a value' }
// Then assign the property
anObject[newPropertyName] = ':D'
// Adding a slightly different property and assigning it
anObject['bigger ' + newPropertyName] = 'XD'
// Result
// {
// aProperty: 'a value',
// 'bigger smile': 'XD'
// smile: ':D',
// }
در ES6 میتوانید زمانی که آبجکت را میسازید، مستقیما اسامی ویژگی داینامیک را اختصاص دهید.
تنها نکتهای که باید رعایت کنید این است که ویژگی داینامیک را در [ ] قرار دهید.
const newPropertyName = 'smile'
// ES6 way.
const anObject = {
aProperty: 'a value',
// Dynamic property names!
[newPropertyName]: ':D',
['bigger ' + newPropertyName]: 'XD',
}
// Result
// {
// aProperty: 'a value',
// 'bigger smile': 'XD'
// smile: ':D',
// }
Template literals
کار کردن با رشتهها در جاوا اسکریپت یک تجربه دست و پا گیر است. قبلتر هنگام تعریف default parameters کمی با رشتهها سر و کار داشتیم. رشتههایی با فضای خالی ایجاد کردیم و با + آن را به هم وصل کردیم.
function announcePlayer (firstName, lastName, teamName) {
console.log(firstName + ' ' + lastName + ', ' + teamName)
}
در ES6 با template literals مشکل برطرف شده است. با استفاده از backticks به یک مکان نگهدارنده ویژه (${}) دسترسی پیدا میکنید که میتوانید به طور عادی از جاوا اسکریپت استفاده کنید.
const firstName = 'Zell'
const lastName = 'Liew'
const teamName = 'unaffiliated'
const theString = `${firstName} ${lastName}, ${teamName}`
console.log(theString)
// Zell Liew, unaffiliated
همانطور که ملاحظه میکنید میتوان همه چیز را با template literals گروهبندی کرد. بهترین بخش template literals این است که میتوانید به راحتی استرینگهای چند خطی ایجاد کنید.
const multi = `One upon a time,
In a land far far away,
there lived a witich,
who could change night into day`
استرینگهای چند خطی به درستی کار میکنند.
یک ترفند به اصطلاح تمیز، استفاده از این رشتهها برای ایجاد کدهای HTML در جاوا اسکریپت است. درست است که شاید بهترین روش نباشد ولی بهتر از ایجاد یک به یک کدهای جاوا اسکریپت و HTML است.
const container = document.createElement('div')
const aListOfItems =
`
Point number one
Point number two
Point number three
Point number four
`
container.innerHTML = aListOfItems
document.body.append(container)
فیچر بعدی تگها هستند. تگها توابعی هستند که اگر خواستید هر استرینگی را جایگزین کنید، اجازه میدهند template literal را دستکاری کنید.
const animal = 'lamb'
// This a tag
const tagFunction = () => {
// Do something here
}
// This tagFunction allows you to manipulate the template literal.
const string = tagFunction `Mary had a little ${animal}`
دیدگاهها
ثبت دیدگاه