قابلیت های جدید جاوا اسکریپت ES2020 — راهنمای کاربردی
در این مقاله میخواهیم برخی از قابلیت های جدید جاوا اسکریپت ES2020 یا ES11 را بررسی کنیم که فهرست آن به شرح زیر است:
- متغیرهای خصوصی کلاس
- متد Promise.allSettled
- متد String.prototype.matchAll
- عملگر زنجیرهسازی بهینه
- ایمپورت دینامیک
- نوع داده BigInt
توجه کنید که همه این قابلیتها روی کروم نسخه 79 تست شدهاند.
متغیرهای خصوصی کلاس
با افزودن یک نماد هش ساده در ابتدای متغیر یا تابع، میتوانیم آنها را به طور کامل برای استفاده از داخلی درون کلاس رزرو کنیم.
1class Person {
2 #born = 1980
3 age() { console.log(2020 - this.#born) }
4}
5const person1 = new Person()
6person1.age() // 40
7console.log(person1.#born)
8//Uncaught SyntaxError: Private field '#born' must be declared in an
9//enclosing class
متد Promise.allSettled
از زمان معرفی Promise-ها در ES6، جاوا اسکریپت از دو ترکیبکننده Promise یعنی متدهای استاتیک Promise.all و Promise.race پشتیبانی میکند. اکنون Promise.allSettled نیز به ES2020 اضافه شده است.
برخلاف Promise.all یا Promise.race که به ترتیب در زمان رد شدن یا قبول شدن Promise-ها یک اتصال کوتاه ایجاد میکرد، Promise.allSettled همه Promise-ها را صرف نظر از رد شدن هر یک از Promise-ها اجرا میکند.
بنابراین میتوانیم به صورت زیر جمعبندی کنیم:
- متد Promise.allSettled اتصال کوتاه نمیکند و در ES2020 اضافه شده است.
- متد Promise.all زمانی که یک مقدار ورودی رد شود، اتصال کوتاه میکند و. در ES6 اضافه شده است.
- متد Promise.race زمانی که یک مقدار ورودی پذیرفته شود، اتصال کوتاه میکند و در ES6 اضافه شده است.
با ورود متد جدید Promise.allSettled اینک میتوانیم یک Promise بسازیم که تنها زمانی که همه Promise-های ارسالی تکمیل شدند، بازگشت یابد. از این رو امکان دسترسی به ماتریسی با دادههای هر Promise را فراهم میسازد.
1const promiseOne = new Promise((resolve, reject) =>
2 setTimeout(resolve, 3000));
3const promiseTwo = new Promise((resolve, reject) =>
4 setTimeout(reject, 3000));
5Promise.allSettled([promiseOne, promiseTwo]).then(data => console.log(data));
6Promise {<pending>}
7//(2) [{…}, {…}]
8//0: {status: "fulfilled", value: undefined}
9//1: {status: "rejected", reason: undefined}
10//length: 2
متد String.prototype.matchAll
با فرض وجود یک رشته و یک «عبارت منظم» (regular expression)، متد ()matchAll یک تکرارکننده از همه نتایج تطبیق یافته رشته در برابر عبارت منظم بازگشت میدهد که شامل «گروههای حاصل» (Capturing Groups) است. ساختار آن به صورت str.matchAll(regexp) است:
- regexp – یک شیء عبارت منظم است.
- مقدار بازگشتی – یک تکرارکننده یا iterator است که تکرارشونده قابل ریاستارت نیست.
گروههای حاصل در زمان استفاده از ()match با فلگ سراسری /g نادیده گرفته میشوند:
1const regexp = /g(ro)(up(\d?))/g;
2const groups = 'group1group2group3';
3groups.match(regexp);
4//(3) ["group1", "group2", "group3"]
5//0: "group1"
6//1: "group2"
7//2: "group3"
با استفاده از matchAll میتوان به گروههای حاصل دسترسی یافت:
1const regexp = /g(ro)(up(\d?))/g;
2const someString = 'group1group2group3';
3const array = [...someString.matchAll(regexp)];
4array
5//(3) [Array(4), Array(4), Array(4)]
6//0: (4) ["group1", "ro", "up1", "1", index: 0, input: //"group1group2group3", groups: undefined]
7//1: (4) ["group2", "ro", "up2", "2", index: 6, input: //"group1group2group3", groups: undefined]
8//2: (4) ["group3", "ro", "up3", "3", index: 12, input: //"group1group2group3", groups: undefined]
9//length: 3
عملگر زنجیرهسازی اختیاری
زنجیرههای طولانی از دسترسی به مشخصهها مستعد بروز خطا هستند و بررسی وجود مشخصه روی هر گام به یک ساختار تودرتوی عمیق منتهی میشود. به مثال زیر که در مورد یک شیء ساده خودرو (Car) است، توجه کنید:
1let car = {
2 engine : {
3 consumption: 10
4 }
5}
فرض کنید میخواهیم به مصرف سوخت (consumption) دسترسی پیدا کنیم، این کار به صورت زیر انجام مییابد:
1let consumption = car.engine.consumption
2//console.log(consumption)
3//10 Ok, no problem here.
اما اگر engine مقدار null داشته باشد، چطور میشود؟ در این صورت اگر تلاش کنیم به consumption دسترسی پیدا کنیم، با یک خطا مواجه خواهیم شد. باید ابتدا مشخصه را بررسی کنیم:
1let car = {
2}
3car.engine.consumption;
4//Uncaught TypeError: Cannot read property 'consumption' of
5//undefined
6//Before ES2020
7//Check if exists
8let consumption = car.engine ? car.engine.consumption : undefined
9//console.log(consumption )
10//undefined
11/////or/////
12let car = {
13}
14//Check if exists
15let consumption;
16if(car.engine && car.engine.consumption){
17 let consumption = cat.engine.consumption
18}else{
19 let consumption = undefined
20}
21console.log(consumption)
22//undefined
کد فوق کار میکند، اما بدیهی است که کد زیر بهتر است:
1let car = {
2}
3let consumption = car.engine?.consumption
4console.log(consumption);
5//undefined
همچنین میتوان از عملگر زنجیرهسازی اختیاری استفاده کرد:
1let car = null;
2let consumption = car?.engine?.consumption
3console.log(consumption);
زنجیرهسازی اختیاری روی آرایهها و فراخوانیهای تابع کار میکند. به مثال زیر توجه کنید:
1//This makes sure that array exists before trying to access the //first element.
2let car1 = array?.[1];
این عملگر زنجیرهسازی اختیاری است و کارکرد ساده و بسیار مفیدی دارد.
ایمپورت دینامیک
()Dynamic import یک شکل شبه تابع از ایمپورت معرفی میکند که قابلیتهای جدیدی در قیاس با ایمپورت استاتیک دارد، مثلاً میتوانیم از آن برای «بارگذاری کند» (lazy loading) استفاده کنیم. مورد زیر را که در فایل greetingsModule.js/. قرار دارد در نظر بگیرید:
1//greetingsModule.js
2export hello () => console.log("Hello World!");
در کد فوق به صورت استاتیک ایمپورت میکنیم و از ماژول greetings Module.js/. استفاده شده است:
1//main.js
2import * as greet from './ greetingsModule.js’;
3greet.hello();
4//Hello World!
ساختار ایمپورت استاتیک تنها در سطح فوقانی فایل میتواند استفاده شود. به جای آن در زمان استفاده از ایمپورت دینامیک عبارت import(module) ماژول را بارگذاری کرده و یک Promise بازگشت میدهد که به صورت یک شیء ماژول درمیآید که شامل همه اکسپورتهایش است. ضمناً میتوانید آن را از هر جایی از کد فراخوانی کنید.
1...
2if( 1 === 1){
3import(’./greetingsModule.js’).then( (greet) => {
4 greet.hello();
5 // Hello World!
6 });
7}
8...
یا میتوانید از async/await استفاده کنید:
1...
2async function load() {
3 let greet = await import(’./greetingsModule.js’);
4 greet.hello();
5 // Hello!
6 }
7...
ایمپورت استاتیک و ایمپورت دینامیک هر دو مفید هستند. هر کدام از آنها کاربرد خاص خود را دارند. از ایمپورتهای استاتیک برای نمونه برای وابستگیهای اولیه استفاده میشود. در موارد دیگر میتوان برخی وابستگیهای بنا به تقاضا را با یک ایمپورت دینامیک بارگذاری کرد.
BigInt
BigInt یک شیء داخلی جدید است که روشی برای نمایش اعداد کامل بزرگتر از فراهم میسازد. انواع عددی در جاوا اسکریپت دارای محدودیت بازه هستند و نمیتوانند با اعداد بزرگ صحیح به درستی کار کنند. قابلیت جدید BigInt این مشکل را که در زمینههای مختلف که با مقادیر علمی سروکار دارند مسئله بزرگی محسوب میشود، حل میکند.
1console.log(Number.MAX_SAFE_INTEGER);
2//9007199254740991
3const max = Number.MAX_SAFE_INTEGER;
4console.log(max +1);
5//9007199254740992 -> Correct value!
6console.log(max +10);
7//9007199254741000 -> Incorrect value! (1001)
این مشکل با استفاده از نوع داده جدید BigInt قابل حل است. با افزودن حرف n به انتهای عدد، کد زیر به دست میآید:
1const myBigNumber = 9007199254740991n;
2console.log(myBigNumber +1n);
3//9007199254740992n -> Correct value!
4console.log(myBigNumber +10n);
5//9007199254741001n -> Correct value!
6//Note:
7console.log(myBigNumber +10);
8//Error: you cannot mix BigInt and other types, use explicit //conversions.
9//Correct way: You have to add the letter 'n' on the end of the //number