ویژگی‌های جدید ES 2019

ترجمه و تالیف : ارسطو عباسی
تاریخ انتشار : 16 اسفند 98
خواندن در 3 دقیقه
دسته بندی ها : جاوا اسکریپت

در این مطلب از وب‌سایت راکت قصد داریم نگاهی به آخرین تغییرات اکمااسکریپت بیاندازیم که معروف به ES 2019 است. این تغییرات در حال حاضر توسط node، Chrome، Firefox و Safari پشتیبانی می‌شود. همچنین می‌توانید از طریق Babel ویژگی‌های آن را به نسخه‌های قدیمی‌تر جاوااسکریپت تبدیل کنید.

حال بیایید ویژگی‌های جدید را بررسی نماییم:

Object.fromEntries

در ES 2017 ويژگی Object.entries را داشتیم. این ویژگی در حقیقت تابعی بود که یک شئ را به آرایه‌ای متناظر با اطلاعات شئ تبدیل می‌کرد. اگر به ساختار زیر دقت کنید متوجه خواهید شد:

let students = {
  amelia: 20,
  beatrice: 22,
  cece: 20,
  deirdre: 19,
  eloise: 21
}

Object.entries(students) 
// [
//  [ 'amelia', 20 ],
//  [ 'beatrice', 22 ],
//  [ 'cece', 20 ],
//  [ 'deirdre', 19 ],
//  [ 'eloise', 21 ]
// ]

می‌توان گفت که این ویژگی به‌راستی یکی از عالی‌ترین توابعی بود که در اکمااسکریپت بوجود آمد چرا که حال شما قابلیت آن‌را داشتید تا بتوانید توابع داخلی مربوط به آرایه‌ها را روی داده‌هایی که به صورت شئ بودند اعمال کنید. منظور از توابع داخلی مواردی مانند map، filter، reduce و… بود. البته مشکل اساسی این حالت آن بود که تبدیل کردن دوباره آرایه به شئ کار سختی بود. برای مثال:

let students = {
  amelia: 20,
  beatrice: 22,
  cece: 20,
  deirdre: 19,
  eloise: 21
}

// convert to array in order to make use of .filter() function
let overTwentyOne = Object.entries(students).filter(([name, age]) => {
  return age >= 21
}) // [ [ 'beatrice', 22 ], [ 'eloise', 21 ] ]

// turn multidimensional array back into an object
let DrinkingAgeStudents = {}
for (let [name, age] of overTwentyOne) {
    DrinkingAgeStudents[name] = age;
}
// { beatrice: 22, eloise: 21 }

Object.fromEntries قابلیت جدیدی است که نیاز ما به حلقه آخر کدهای بالا را رفع می‌کند. در حقیقت این متد در ادامه متد قبلی فرایند اعمال توابع آرایه روی شئ و برگرداندن خروجی به عنوان یک شئ را بسیار ساده‌تر می‌کند. 

let students = {
  amelia: 20,
  beatrice: 22,
  cece: 20,
  deirdre: 19,
  eloise: 21
}

// convert to array in order to make use of .filter() function
let overTwentyOne = Object.entries(students).filter(([name, age]) => {
  return age >= 21
}) // [ [ 'beatrice', 22 ], [ 'eloise', 21 ] ]

// turn multidimensional array back into an object
let DrinkingAgeStudents = Object.fromEntries(overTwentyOne); 
// { beatrice: 22, eloise: 21 }

البته به عنوان یک نکته مهم نیاز است بگویم که آرایه و شئ دو ساختار داده‌ای متفاوت هستند. از این‌رو ممکن است در برخی حالت‌ها، فرایند تبدیل با از دست رفتن برخی از داده‌ها همراه باشد. برای مثال در کدهای زیر ما یک آرایه داریم که در آن یک عضو تکراری وجود دارد در این حالت اگر آرایه را به شئ تبدیل کنیم آن عضو تکراری حذف خواهد شد:

let students = [
  [ 'amelia', 22 ], 
  [ 'beatrice', 22 ], 
  [ 'eloise', 21], 
  [ 'beatrice', 20 ]
]

let studentObj = Object.fromEntries(students); 
// { amelia: 22, beatrice: 20, eloise: 21 }
// dropped first beatrice!

بنابراین زمانی که قصد استفاده از این تابع را دارید از اثرات جانبی آن مطلع شوید. همان‌طور هم که گفته شد این قابلیت در مرورگرهای کروم، فایرفاکس و سافاری پشتیبانی می‌شود.

Array.prototype.flat

آرایه‌های چند بعدی یکی از گزینه‌هایی است که در بحث ساختمان‌های داده بسیار از آن استفاده می‌شود. یکی از موضوعاتی که در تعامل با این دسته از آرایه‌ها معمولاً از آن استفاده می‌شود قابلیت یکسان‌سازی است. می‌توان این کار را به صورت دستی انجام داد اما به نظر نمی‌رسد که رویکردهای قبل از ES 2019 چندان بهینه و مناسب باشند. برای مثال کدهای زیر را در نظر بگیرید:

let courses = [
  {
    subject: "math",
    numberOfStudents: 3,
    waitlistStudents: 2,
    students: ['Janet', 'Martha', 'Bob', ['Phil', 'Candace']]
  },
  {
    subject: "english",
    numberOfStudents: 2,
    students: ['Wilson', 'Taylor']
  },
  {
    subject: "history",
    numberOfStudents: 4,
    students: ['Edith', 'Jacob', 'Peter', 'Betty']
  }
]

let courseStudents = courses.map(course => course.students)
// [
//   [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
//   [ 'Wilson', 'Taylor' ],
//   [ 'Edith', 'Jacob', 'Peter', 'Betty' ]
// ]

[].concat.apply([], courseStudents) // we're stuck doing something like this

یک ساختار جایگزین برای این حالت می‌تواند متد جدیدی باشد که در ES 2019 معرفی شده است. این متد Array.prototype.flat نام دارد:

let courseStudents = [
  [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
  [ 'Wilson', 'Taylor' ],
  [ 'Edith', 'Jacob', 'Peter', 'Betty' ]
]

let flattenOneLevel = courseStudents.flat(1)
console.log(flattenOneLevel)
// [
//   'Janet',
//   'Martha',
//   'Bob',
//   [ 'Phil', 'Candace' ],
//   'Wilson',
//   'Taylor',
//   'Edith',
//   'Jacob',
//   'Peter',
//   'Betty'
// ]

let flattenTwoLevels = courseStudents.flat(2)
console.log(flattenTwoLevels)
// [
//   'Janet',   'Martha',
//   'Bob',     'Phil',
//   'Candace', 'Wilson',
//   'Taylor',  'Edith',
//   'Jacob',   'Peter',
//   'Betty'
// ]

فرایند یکسان‌سازی با استفاده از این متد همراه با میزان عمق آن خواهد بود. اگر مقدار ۱ را در ورودی این تابع قرار دهید عمق یکسان‌سازی‌تان برابر با یک خواهد بود. مقدار پیش‌فرض نیز همین است. بدین معنا که اگر شما ورودی را وارد نکنید عمق یکسان‌سازی برابر با یک خواهد بود. 

زمانی که از آیتم‌های موجود در آرایه با خبر نیستید اما می‌خواهید بهترین سطح از عمق را برای یکسان‌سازی در نظر بگیرید می‌توانید مقدار infinity را در ورودی تابع قرار دهید.

let courseStudents = [
  [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
  [ 'Wilson', 'Taylor' ],
  [ 'Edith', 'Jacob', 'Peter', 'Betty' ]
]

let defaultFlattened = courseStudents.flat()
console.log(defaultFlattened)
// [
//   'Janet',
//   'Martha',
//   'Bob',
//   [ 'Phil', 'Candace' ],
//   'Wilson',
//   'Taylor',
//   'Edith',
//   'Jacob',
//   'Peter',
//   'Betty'
// ]

پشتیبانی از این قابلیت را می‌توانید در مرورگرهای کروم، فایرفاکس و سافاری مشاهده کنید.

Array.prototype.flatMap

این ویژگی جدید نیز در ارتباط با ویژگی قبلی‌ست با این تفاوت که در این مورد می‌توان کاربرد جدیدی را مشاهده کرد. حالتی را در نظر بگیرید که ما در روال برنامه قصد داریم تا به آرایه عناصر جدیدی را اضافه کنیم. اگر با چنین سناریویی قبل از ES 2019 برخورد بکنیم باید به صورت زیر عمل نماییم:

let grades = [78, 62, 80, 64]

let curved = grades.map(grade => [grade, grade + 7])
// [ [ 78, 85 ], [ 62, 69 ], [ 80, 87 ], [ 64, 71 ] ]

let flatMapped = [].concat.apply([], curved) // now flatten, could use flat but that didn't exist before either
// [
//  78, 85, 62, 69,
//  80, 87, 64, 71
// ]

حال که ما Array.prototype.flat را در اختیار داریم می‌توانیم روشی بهینه‌تر را در نظر بگیریم:

let grades = [78, 62, 80, 64]

let flatMapped = grades.map(grade => [grade, grade + 7]).flat()
// [
//  78, 85, 62, 69,
//  80, 87, 64, 71
// ]

البته این حالت هنوز هم کاملاً روش مستقلی نیست چرا که ما در حال استفاده از متد map هستیم. اما رویکرد دیگری که می‌توانیم پیش ببریم حالت زیر خواهد بود:

let grades = [78, 62, 80, 64]

let flatMapped = grades.flatMap(grade => [grade, grade + 7]);
// [
//  78, 85, 62, 69,
//  80, 87, 64, 71
// ]

در نظر داشته باشید که مقدار پیش‌فرض این تابع یک خواهد بود. در حقیقت flatMap معادل با ترکیبی از map و flat بدون ورودی است. از این رو flatMap تنها یک سطح از یکسان‌سازی را انجام می‌دهد.

این قابلیت نیز مانند حالت‌های قبل در کروم، فایرفاکس و سافاری پشتیبانی می‌شود.

String.trimStart و String.trimEnd

یکی دیگر از ویژگی‌های زیبا ES 2019 ایجاد دو نام مستعار معنادار برای دو تابع قدیمی است. قبلاً ما از String.trimRight و String.trimLeft استفاده می‌کردیم:

let message = "   Welcome to CS 101    "
message.trimRight()
// '   Welcome to CS 101'
message.trimLeft()
// 'Welcome to CS 101   '
message.trimRight().trimLeft()
// 'Welcome to CS 101'

این موارد توابع عالی هستند اما ایکاش می‌توانستیم نام‌های بهتری را برای آن‌ها در نظر بگیریم. چرا که trimRight و trimLeft نام‌های چندان جذابی نیستند. در ES 2019 این مشکل با معرفی trimEnd و trimStart حل شده است:

let message = "   Welcome to CS 101    "
message.trimEnd()
// '   Welcome to CS 101'
message.trimStart()
// 'Welcome to CS 101   '
message.trimEnd().trimStart()
// 'Welcome to CS 101'

این ویژگی نیز توسط مرورگرهای کروم، فایرفاکس و سافاری پشتیبانی می‌شود.

اختیاری شدن آرگومان try-catch

یکی دیگر از ویژگی‌های جذاب مربوط به ES 2019 اختیاری شدن آرگومان مربوط به بلوک‌های try-catch است. در نسخه‌های قبلی اکمااسکریپت تمام استثنائات به صورت یک پارامتر در می‌آمد. این بدان معناست که حتی زمانی که catch نادیده گرفته می‌شد این پارامتر هنوز وجود داشت:

try {
  let parsed = JSON.parse(obj)
} catch(e) {
  // ignore e, or use
  console.log(obj)
}

اما حال دیگر نیازی به چنین حالتی نیست. 

try {
  let parsed = JSON.parse(obj)
} catch {
  console.log(obj)
}

زمانی‌که بدرستی از خطا شناخت دارید و می‌دانید چه مسئله‌ای پیش آمده استفاده از این حالت می‌تواند مناسب باشد.

این ویژگی نیز توسط مرورگرهای کروم، فایرفاکس و سافاری پشتیبانی می‌شود.

تغییرات در تابع toString

در ES 2019 می‌توان تغییراتی در ارتباط با Function.toString() را مشاهده کرد. قبلاً اگر از این قابلیت استفاده می‌کردید تنها جزئیات مربوط به تابع نشان داده می‌شد:

function greeting() {
  const name = 'CSS Tricks'
  console.log(`hello from ${name}`)
}

greeting.toString()
//'function greeting() {\nconst name = \'CSS Tricks\'\nconsole.log(`hello from ${name} //`)\n}'

حال اگر در ES 2019 از آن استفاده کنید می‌توانید به صورت ساختارمندتری خروجی را مشاهده کنید:

function greeting() {
  const name = 'CSS Tricks'
  console.log(`hello from ${name}`)
}

greeting.toString()
// 'function greeting() {\n' +
//  "  const name = 'CSS Tricks'\n" +
//  '  console.log(`hello from ${name}`)\n' +
//  '}'

می‌توانید پشتیبانی از این قابلیت را در مرورگرهای کروم، فایرفاکس، سافاری و اج مشاهده کرد.

در پایان

این موارد تقریباً تمام ویژگی‌های مهمی بودند که در ES 2019 معرفی شدند. البته می‌توانید در لینک‌های زیر نیز اطلاعات بیشتری در ارتباط با این نسخه پیدا کنید:

منبع

گردآوری و تالیف ارسطو عباسی

من ارسطو‌ام :) کافی نیست؟! :)