روز به روز اهمیت استفاده از جاوا اسکریپت (JavaScript) پررنگتر میشود و از همین روی دولوپرهایی که با ترفندهای بیشتری از این زبان آشنا باشند میتوانند گوی سبقت را از دیگران بربایند. بنابراین در این مقاله به معرفی ترفندهایی از ES6 (اکما اسکریپت ۶) میپردازیم که با استفاده از آنها خواهید توانست کدهای جاوا اسکریپت بهتر و بهینهتری بنویسید.
در این مقاله سعی شده تا ویژگیهای کلیدی ES6 به طور مختصر بیان شود به طوری که میتوان گفت در پایان این آموزش مهارتهای پایهای کار با این نسخه از زبان محبوب جاوااسکریپت را یاد گرفته و قادر خواهید بود آنها را در پروژههای واقعی اِعمال کنید. همچنین در نظر داشته باشید که این آموزش را نمیبایست به عنوان یک راهنما یا داکیومنشن JS تلقی کنید؛ بلکه هدف اصلی در اینجا تشویق شما به عمیقتر شدن و آشنایی بیشتر با ES6 است (لازم به ذکر است برای تست کدها، علاوه بر کنسول مرورگری همچون گوگل کروم میتوانید از وبسایت https://jsconsole.com نیز میتوانید استفاده نمایید.)
1. کلمات کلیدی const و let در اکما اسکریپت 6
کلیدواژهٔ const
به شما امکان تعریف کانستَنتها را میدهد (خودِ این کلمه کوتاهشدۀ Constant به معنای «ثابت» است.) و با let
هم میتوان دست به تعریف متغیر زد اما ممکن است این سؤال برایتان پیش بیاید که مگر پیش از این در زبان جاوااسکریپت امکان تعریف متغیر را نداشتیم؟
درست است اما متغیرهایی که با var
تعریف میشوند اِسکوپ وسیعی دارند اما آنچه از طریق let و const در جاوا اسکریپت ایجاد میگردد، اِسکوپ (محدوده) مشخصی خواهد داشت که همین مسئله احتمال ایجاد باگ را به حداقل میرساند:
function f() {
var x = 1
let y = 2
const z = 3
{
var x = 100
let y = 200
const z = 300
console.log('x in block scope is', x)
console.log('y in block scope is', y)
console.log('z in block scope is', z)
}
console.log('x outside of block scope is', x)
console.log('y outside of block scope is', y)
console.log('z outside of block scope is', z)
}
f()
به عنوان خروجی اسکریپت فوق داریم:
x in block scope is 100
y in block scope is 200
z in block scope is 300
x outside of block scope is 100
y outside of block scope is 2
z outside of block scope is 3
به طور کلی، مهمترین تفاوت بین متغیرهایی که با let
و const
تعریف میشوند با متغیرهایی که با var
تعریف میشوند قابلیت دسترسی آنها است. به عبارت دیگر، متغیرهایی که با let
و const
تعریف گردند تنها در داخل بلوکهایی که تعریف شدهاند اعتبار دارند (در جاوا اسکریپت یک بلوک از کد با علائم آکولاد مشخص میشود. به طور مثال، در کد بالا میبینید که x
در بیرون از بلوک دارای مقدار 1 است و در درون بلوک مقداری معادل با 100 دارد و وقتی میخواهیم x
را چه در داخل بلوک چه در بیرون بلوک در کنسول نشان دهیم، عدد 100 را نمایش میدهد در حالی که مقدار متغیرهای تعریف شده توسط let
و const
در داخل و بیرون بلوک با هم تفاوت دارند.)
💎💎 برای یادگیری کامل مفاهیم اولیه برنامه نویسی با زبان جاوا اسکریپت، به دوره آموزش جاوا اسکریپت - از پایه تا پروژه ؛ و برای یادگیری مفاهیم پیشرفته تر JavaScript، به دوره آموزش جاوا اسکریپت پیشرفته مراجعه کنید.
2. هِلپِرهای مرتبط با آرایه
یکسری هِلپِر فانکشن جدید در اکما اسکریپت 6 عرضه شده است که کار با آرایههای جاوا اسکریپت را در بسیاری از موارد آسان میکنند. تا به حال چندین بار برایتان پیش آمده که بخواهید کارهایی همچون فیلتر کردن، چک کردن همهٔ اِلِمانهای آرایه مطابق یک شرط و یا تغییر اِلِمانها را انجام دهید؟ در اکما اسکریپت 6 به فیچرهایی کاربردی از این زبان دسترسی دارید که این کارها را برایتان انجام میدهد. برای مثال، در ادامه چند نمونه از این هِلپِرها را معرفی کردهایم:
()forEech
خروجی این فانکشن روی هر اِلِمان از آرایه اِعمال میشود. به عبارت دیگر، هر اِلِمان از آرایه را به عنوان یک مقدار یا آرگومان پاس میدهد:
var colors = ['red', 'green', 'blue']
function print(val) {
console.log(val)
}
colors.forEach(print)
به عنوان خروجی داریم:
red
green
blue
برای درک بهتر این موضوع، فرض کنید هر فانکشن مانند یک کارخانه میماند و وقتی ورودیها به این کارخانه وارد میشوند، فانکشن ()forEach
به جای اینکه روی کل ورودی به یکباره اِعمال شود، روی تکتک ورودیها اِعمال میگردد. در مثال بالا، آرایهٔ colors
دارای مقادیر green ،red و blue است و وقتی فانکشن ()print
استفاده شود، کل خانههای این آرایه را به یکباره و با هم نشان میدهد یعنی خروجی به صورت پیوسته red green blue را نشان خواهد داد اما وقتی فانکشن ()forEach
استفاده شود، این بدان معنا است که فانکشن ()print
برای هر اِلِمان اِعمال میگردد و بدین ترتیب فانکشن ()print
به طور جداگانه یک بار برای red، یک بار برای green و یک بار هم برای blue اجرا خواهد گشت.
💎در مورد انواع حلقه های for در جاوا اسکریپت ، میتوانید مقاله ای را با همین نام در سکان آکادمی مطالعه کنید.
()map
در اکما اسکریپت 6، فانکشن ()map
، آرایهای جدید با همان اِلِمانها ایجاد میکند به طوری که این فانکشن فقط هر یک از اِلِمانهای آرایه را به چیز دیگری تغییر میدهد:
var colors = ['red', 'green', 'blue']
function capitalize(val) {
return val.toUpperCase()
}
var capitalizedColors = colors.map(capitalize)
console.log(capitalizedColors)
به عنوان خروجی داریم:
["RED","GREEN","BLUE"]
در مثال بالا آرایهٔ colors
دارای مقادیر green ،red و blue است که با حروف کوچک نوشته شدهاند اما با استفاده از ()map
گفتهایم که این سه رنگ با حروف بزرگ نوشته شوند؛ یعنی فقط مقادیر آرایه را تغییر داده است و از حروف کوچک به حروف بزرگ تبدیل کرده است.
()filter
این فانکشن یک آرایهٔ جدید شامل بخشی از آرایهٔ اصلی را میسازد. به عبارت دیگر، یک آرایۀ جدید حاوی زیرمجموعهای از آرایه اصلی ایجاد میکند:
var values = [1, 60, 34, 30, 20, 5]
function lessThan20(val) {
return val < 20
}
var valuesLessThan20 = values.filter(lessThan20)
console.log(valuesLessThan20)
به عنوان خروجی داریم:
[1,5]
در کد بالا، آرایهٔ value
شامل تعدادی اعداد است که در ادامه خواسته شده تا اعداد کمتر از 20 را نشان دهد. به عبارتی دیگر، میخواهیم این اعداد را از یک فیلتر، که اینجا اعداد کمتر از 20 است، عبور دهیم.
()find
در اکما اسکریپت 6، این فانکشن اولین اِلِمانی که در شرط قبول شود را پیدا کرده و در پایان خروجی true
یا false
را برمیگرداند:
var people = [
{name: 'Jack', age: 50},
{name: 'Michael', age: 9},
{name: 'John', age: 40},
{name: 'Ann', age: 19},
{name: 'Elisabeth', age: 16}
]
function teenager(person) {
return person.age > 10 && person.age < 20
}
var firstTeenager = people.find(teenager)
console.log('First found teenager:', firstTeenager.name)
به عنوان خروجی داریم:
First found teenager: Ann
در مثال بالا، آرایهٔ people
دارای نام و سن افراد است و در ادامه خواسته شده افراد نوجوان را نشان دهد و گفته شده که افراد نوجوان کسانی هستند که سن بیش از 10 و کمتر از 20 دارند. با استفاده از ()find
اولین نوجوان، که Ann است، را برمیگرداند و با استفاده از ()console.log
در خروجی نشان میدهد.
()every
این فانکشن هر اِلِمان از آرایه را چک میکند تا ببیند از تستی که توسط فانکشن ارائه شده است عبور میکند یا خیر و در پایان true
یا false
را برمیگرداند:
var people = [
{name: 'Jack', age: 50},
{name: 'Michael', age: 9},
{name: 'John', age: 40},
{name: 'Ann', age: 19},
{name: 'Elisabeth', age: 16}
]
function teenager(person) {
return person.age > 10 && person.age < 20
}
var everyoneIsTeenager = people.every(teenager)
console.log('Everyone is teenager: ', everyoneIsTeenager)
به عنوان خروجی داریم:
Everyone is teenager: false
کد بالا هم مانند مثال قبلی است با این تفاوت که خواسته شده تا تست روی هر اِلِمان از آرایه اِعمال شود. بدین منظور، برای فردی با نام Jack چون سنش در محدودهٔ سن نوجوان که از قبل تعریف شده قرار نگرفته است، false
را برگردانده است. به عبارتی، فرق ()find
با ()every
در اینجا است که ()find
اولین اِلِمانی که تست در آن صادق باشد را نشان میدهد اما ()every
روی هر اِلِمان اِعمال میگردد.
()some
این فانکشن در اکما اسکریپت 6 چک میکند که آیا برخی از اِلِمانهای آرایه توسط فانکشن ارائه شده قبول میشوند یا خیر که در غیر این صورت مقدار false
را ریترن میکند:
var people = [
{name: 'Jack', age: 50},
{name: 'Michael', age: 9},
{name: 'John', age: 40},
{name: 'Ann', age: 19},
{name: 'Elisabeth', age: 16}
]
function teenager(person) {
return person.age > 10 && person.age < 20
}
var thereAreTeenagers = people.some(teenager)
console.log('There are teenagers:', thereAreTeenagers)
به عنوان خروجی داریم:
There are teenagers: true
تا اینجا به فانکشنهایی اشاره شد که چک میکردند آیا مقادیر آرایه در تست قبول میشوند یا خیر اما فانکشن ()some
دنبال مقادیری میگردد که درون تست قبول نمیشوند. در مثال بالا، Jack در محدودهٔ سنی نوجوانها قرار نمیگیرد پس مورد قبول نیست و عبارت true
را برمیگرداند.
()reduce
زمانی از ()reduce
استفاده میکنیم که یک آرایه داریم و میخواهیم تمام مقادیرش را جمع کنیم. در اینجا به جای اینکه از حلقه برای جمع مقادیر استفاده کنیم، از فانکشن ()reduce
استفاده میکنیم:
var array = [1, 2, 3, 4]
function sum(acc, value) {
return acc + value
}
function product(acc, value) {
return acc * value
}
var sumOfArrayElements = array.reduce(sum, 0)
var productOfArrayElements = array.reduce(product, 1)
console.log('Sum of', array, 'is', sumOfArrayElements)
console.log('Product of', array, 'is', productOfArrayElements)
به عنوان خروجی داریم:
Sum of [1,2,3,4] is 10
Product of [1,2,3,4] is 24
در این کد، آرایهای داریم تحت عنوان array
که حاوی تعدادی عدد است. acc
چپترین عدد یعنی 1 است و value
عدد سمت راست acc
است (یعنی عدد 2) که برای جمع کردن مقادیر هم دستور داده شده که هر بار مقدار value
را با acc
جمع شود که برای همین منظور برای بار اول 1 با 2 جمع میشود و حاصل 3 میشود. value
یک عدد به سمت راست حرکت میکند و روی 3 قرار میگیرد و این بار 3 را با 3 جمع میکند و حاصل 6 را در acc
ذخیره میکند و برای آخرین عدد، 4 را با 6 جمع کرده و حاصل 10 را نگاه میدارد. حال چون دیگر عددی باقی نمانده، حاصل 10 را که ذخیره کرده بود نشان میدهد.
3. Arrow Function - فانکشنهای <=
اجرای فانکشنهای بسیار ساده مانند ()sum
یا ()product
که در مثالهای قبلی ذکر شد، نیاز به کدهای تکراری زیادی دارد. برای رفع این معضل، میتوان از مفهومی تحت عنوان Arrow Function استفاده کرد که با استفاده از این نوع فانکشنها میتوان کدها را کوتاهتر نوشت. به عنوان نمونه، مثال قبلی که در دوازده خط نوشته شده بود، در کد زیر با هفت خط نوشته شده است:
var array = [1, 2, 3, 4]
const sum = (acc, value) => acc + value
const product = (acc, value) => acc * value
var sumOfArrayElements = array.reduce(sum, 0)
var productOfArrayElements = array.reduce(product, 1)
همچنین میتوان این نوع فانکشنها را به صورت خطی نوشت که کد را باز هم سادهتر میکنند:
var array = [1, 2, 3, 4]
var sumOfArrayElements = array.reduce((acc, value) => acc + value, 0)
var productOfArrayElements = array.reduce((acc, value) => acc * value, 1)
نیاز به توضیح است که Arrow Function همچنین میتواند پیچیده بوده و خطوط کد زیادی را شامل گردد:
var array = [1, 2, 3, 4]
const sum = (acc, value) => {
const result = acc + value
console.log(acc, ' plus ', value, ' is ', result)
return result
}
var sumOfArrayElements = array.reduce(sum, 0)
💎 پیشنهاد میکنیم برای استفاده بهتر از توابع Arrow ، مقالهی "best practiceهایی برای نوشتن arrow function های بهتر در جاوا اسکریپت" را نیز مطالعه کنید.
4. کلاسها در اکما اسکریپت 6
دولوپرهایی که تجربهٔ کدنویسی با سایر زبانهای شیئگرا مثل جاوا، پیاچپی، سیشارپ و غیره را دارند و شروع به کدنویسی با زبان جاوا اسکریپت میکنند، مسلماً خلاء نبود کلاسها را احساس کردهاند و همین مسئله منجر به این گشت تا توسعهدهندگان زبان محبوب جاوااسکریپت مفهوم کلاس را در نسخهٔ اکما اسکریپت 6 - ES6 این زبان بگنجانند. در واقع، در این نسخه مفهوم وراثت تغییری نکرده بلکه تنها سینتکس بهتری برای ارثبری پروتوتایپها ارائه شده است:
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
toString() {
return '[X=' + this.x + ', Y=' + this.y + ']'
}
}
class ColorPoint extends Point {
static default() {
return new ColorPoint(0, 0, 'black')
}
constructor(x, y, color) {
super(x, y)
this.color = color
}
toString() {
return '[X=' + this.x + ', Y=' + this.y + ', color=' + this.color + ']'
}
}
console.log('The first point is ' + new Point(2, 10))
console.log('The second point is ' + new ColorPoint(2, 10, 'green'))
console.log('The default color point is ' + ColorPoint.default())
به عنوان خروجی داریم:
The first point is [X=2, Y=10]
The second point is [X=2, Y=10, color=green]
The default color point is [X=0, Y=0, color=black]
5. بهبود مفهوم آبجکت لیترال
در اکما اسکریپت 6 - ES6 مفهوم آبجکت لیترال بهبود یافته است به طوری که از این پس میتوانیم کارهایی همچون تعریف فیلدها با اختصاص دادن متغیرهایی با همان نام، تعریف فانکشنها و تعریف پِراپِرتیهای داینامیک (پویا) را بسیار راحتتر انجام دهیم:
const color = 'red'
const point = {
x: 5,
y: 10,
color,
toString() {
return 'X=' + this.x + ', Y=' + this.y + ', color=' + this.color
},
[ 'prop_' + 42 ]: 42
}
console.log('The point is ' + point)
console.log('The dynamic property is ' + point.prop_42)
به عنوان خروجی داریم:
The point is X=5, Y=10, color=red
The dynamic property is 42
6. اِسترینگ تِمپلیت در اکما اسکریپت 6
کمتر دولوپری را میتوان یافت که علاقه داشته باشد با وصل کردن متغیرهای مختلف، استرینگهای طولانی ایجاد کند و این در حالی است که خیلی از دولوپرها از خواندن سورسکدهایی که به اینگونه نوشته شدهاند هم تنفر دارند. ES6 تِمپلیتهای بسیار سادهای برای استفاده از استرینگها به همراه Placeholder برای متغیرها معرفی کرده است:
function hello(firstName, lastName) {
return `Good morning ${firstName} ${lastName}!
How are you?`
}
console.log(hello('Jan', 'Kowalski'))
به عنوان خروجی داریم:
Good morning Jan Kowalski!
How are you?
7. آرگومانهای دیفالت فانکشنها
اگر تمایلی نداشته باشید تا همهٔ پارامترهای ممکن فانکشن را ذکر کنید، میتوانید در نسخهٔ اکما اسکریپت 6 از مقادیر دیفالت استفاده کنید:
function sort(arr = [], direction = 'ascending') {
console.log('I\'m going to sort the array', arr, direction)
}
sort([1, 2, 3])
sort([1, 2, 3], 'descending')
به عنوان خروجی داریم:
I'm going to sort the array [1,2,3] ascending
I'm going to sort the array [1,2,3] descending
8. اپراتور ...
در اکما اسکریپت 6، علائم ... میتوانند جهت ایجاد یک کپی از روی چیزی همچون یک آرایه مورد استفاده قرار گیرد:
var array = ['red', 'blue', 'green']
var copyOfArray = [...array]
console.log('Copy of', array, 'is', copyOfArray)
console.log('Are', array, 'and', copyOfArray, 'same?', array === copyOfArray)
به عنوان خروجی داریم:
Copy of ["red","blue","green"] is ["red","blue","green"]
Are ["red","blue","green"] and ["red","blue","green"] same? false
به منظور ادغام آرایهها هم خواهیم داشت:
var defaultColors = ['red', 'blue', 'green']
var userDefinedColors = ['yellow', 'orange']
var mergedColors = [...defaultColors, ...userDefinedColors]
console.log('Merged colors', mergedColors)
به عنوان خروجی داریم:
Merged colors ["red","blue","green","yellow","orange"]
اگر بخواهیم چند پارامتر اولیهٔ فانکشنی را به متغیرها متصل کنیم و پارامترهای دیگر که باقی میمانند را به عنوان آرایه به متغیرهای تکی متصل کنیم، در ES6 به راحتی چنین امکانی فراهم شده است:
function printColors(first, second, third, ...others) {
console.log('Top three colors are ' + first + ', ' + second + ' and ' + third + '. Others are: ' + others)
}
printColors('yellow', 'blue', 'orange', 'white', 'black')
به عنوان خروجی داریم:
Top three colors are yellow, blue and orange. Others are: white,black
در کد بالا، پارمترهای اول، دوم و سوم که به ترتیب blue ،yellow و orange میباشند را به متغیرها متصل کرده و هرچه باقی مانده را به عنوان یک آرایه در متغیرهای تکی گنجاندهایم.
9. Destructuring در اکما اسکریپت 6
این مفهوم میتواند اِلِمانهای درخواستی از آرایه را استخراج کند (بیرون بکشد) و به متغیرها اختصاص دهد:
function printFirstAndSecondElement([first, second]) {
console.log('First element is ' + first + ', second is ' + second)
}
function printSecondAndFourthElement([, second, , fourth]) {
console.log('Second element is ' + second + ', fourth is ' + fourth)
}
var array = [1, 2, 3, 4, 5]
printFirstAndSecondElement(array)
printSecondAndFourthElement(array)
به عنوان خروجی داریم:
First element is 1, second is 2
Second element is 2, fourth is 4
علاوه بر این مفهوم object destructuring در جاوا اسکریپت، میتواند پِراپِرتیهای درخواستی از آبجکت را استخراج کند و آنها را به متغیرهایی با اسامی مشابه به عنوان پِراپِرتی اختصاص دهد:
function printBasicInfo({firstName, secondName, profession}) {
console.log(firstName + ' ' + secondName + ' - ' + profession)
}
var person = {
firstName: 'John',
secondName: 'Smith',
age: 33,
children: 3,
profession: 'teacher'
}
printBasicInfo(person)
به عنوان خروجی داریم:
John Smith - teacher
10. Promise در اکما اسکریپت 6
در برخی از پروژهها نیاز است تا یکسری از عملیات به ترتیب انجام گیرند (مثلاً تَسک اول انجام شود و پس از آن تَسک دوم و الیآخر) اما در بسیاری از پروژهها مدیریت این دست کارها به سرعت از کنترل خارج شده و خواندن سورسکد سخت میشود که برای حل مشکل فراخوانی کدهای همزمان، از مفهومی تحت عنوان Promise در جاوا اسکریپت استفاده میشود.
Promise یکسری قرارداد است که نتایج تَسکهایی که اجرای آنها زمان زیادی طول میکشد را در آینده نشان میدهد که دارای دو نوع ورودی است: ورودی اول برای نتایج و ورودی دوم برای خطاهای احتمالی که برای به دست آوردن نتایج باید یک فانکشن به اصطلاح Callback را به عنوان پارامتر then
قرار دهید و برای مدیریت خطاها هم باید فانکشن Callback را به عنوان پارامتر catch
قرار داد:
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const result = Math.random();
result > 0.5 ? resolve(result) : reject('Oppps....I cannot calculate')
}, 1)
});
}
for (let i=0; i<10; i++) {
asyncFunc()
.then(result => console.log('Result is: ' + result))
.catch(result => console.log('Error: ' + result))
}
توجه داشته باشید از آنجا که فانکشن تصادفی فراخوانی میشود، خروجی در هر بار اجرای کد ممکن است متفاوت باشد. به عنوان خروجی داریم:
Result is: 0.7930997430022211
Error: Oppps....I cannot calculate
Result is: 0.6412258210597288
Result is: 0.7890325910244533
Error: Oppps....I cannot calculate
Error: Oppps....I cannot calculate
Result is: 0.8619834683310168
Error: Oppps....I cannot calculate
Error: Oppps....I cannot calculate
Result is: 0.8258410427354488
چه ویژگیهای دیگری از ES6 - اکما اسکریپت 6 سراغ دارید که کدنویسی دولوپرهای جاوا اسکریپت را به مراتب راحتتر کرده است؟ نظرات، دیدگاهها و تجربیات خود را با سایر کاربران سکان آکادمی به اشتراک بگذارید.