معرفی ویژگی های ES10 – راهنمای کاربردی


ES10 در حال حاضر صرفاً یک نسخه پیشنویس محسوب میشود. اما اغلب ویژگیها به جز Object.fromEntries هم اینک در کروم پیادهسازی شدهاند، بنابراین چرا نباید آن را از قبل مورد بررسی قرار دهیم؟ بدین ترتیب زمانی که مرورگرها شروع به پشتیبانی از آن بکنند، شما از قبل اطلاعات لازم را خواهید داشت. این راهنما برای افرادی که به بررسی ویژگی های ES10 نیاز دارند ضروری خواهد بود.
BigInt - اعداد صحیح با دقت دلخواه
BigInt هفتمین نوع primitive است. BigInt یک عدد صحیح با دقت دلخواه است.
این بدان معنی است که متغیرها میتوانند اینک عدد را نمایش دهند و محدود به عدد 9007199254740992 نیستند.
const b = 1n; // append n to create a BigInt
در گذشته مقادیر صحیح بالاتر از 9007199254740992 پشتیبانی نمیشدند و در صورت تجاوز از این عدد مقدار مورد نظر به صورت MAX_SAFE_INTEGER + 1 قفل میشد.
const limit = Number.MAX_SAFE_INTEGER; ⇨ 9007199254740991 limit + 1; ⇨ 9007199254740992 limit + 2; ⇨ 9007199254740992 <--- MAX_SAFE_INTEGER + 1 exceeded const larger = 9007199254740991n; ⇨ 9007199254740991n const integer = BigInt(9007199254740991); // initialize with number ⇨ 9007199254740991n const same = BigInt("9007199254740991"); // initialize with "string" ⇨ 9007199254740991n
Typeof
typeof 10; ⇨ 'number' typeof 10n; ⇨ 'bigint'
عملگرهای تساوی میتوانند بین این دو نوع استفاده شوند:
10n === BigInt(10); ⇨ true 10n == 10; ⇨ true
عملگرهای ریاضیاتی تنها درون نوع خاص خود عمل میکنند:
200n / 10n ⇨ 20n 200n / 20 ⇨ Uncaught TypeError: Cannot mix BigInt and other types، use explicit conversions <
علامت - ابتدایی کار میکند؛ اما علامت + ابتدایی کار نمیکند:
-100n ⇨ -100n +100n ⇨ Uncaught TypeError: Cannot convert a BigInt value to a number
در زمانی که این مقاله را میخوانید، احتمالاً matchAll به صورت رسمی در کروم نسخه C73 پیادهسازی شده است؛ اما اگر چنین نباشد نیز به خصوص اگر به «عبارتهای منظم» (regex) علاقهمند باشید، بررسی آن ارزشش را دارد.
()string.prototype.matchAll
اگر در گوگل به دنبال عبارت javascript string match all بگردید، احتمالاً نخستین نتیجه، چیزی مانند زیر خواهد بود:
How do I write a regular expression to “match all”?
نتایج برتر استفاده از String.match را به همراه عبارت منظم g/ پیشنهاد میکنند. همچنین توصیه میشود که RegExp.exec و یا RegExp.test با g/ استفاده شود. ابتدا نگاهی به طرز کار مشخصات قدیمی میاندازیم.
String.match با آرگومان string تنها مطابقت first را بازگشت میدهد:
let string = “Hello”; let matches = string.match(“l”); console.log(matches[0]); // "l"
نتیجه کار یک «1» منفرد است. دقت کنید که مطابقت در [matches[0 و نه در matches ذخیره میشود. تنها «1» از جستجو برای «1» در کلمه “hello” بازگشت مییابد. همین نتیجه با استفاده از string.match به همراه یک آرگومان regex بازگشت مییابد. ما میتوانیم کاراکتر «1» را در رشته «hello» با استفاده از عبارت منظم /l/ پیدا کنیم:
let string = "Hello"; let matches = string.match(/l/); console.log(matches[0]); // "l"
افزودن g/ به ترکیب
اما String.match با یک عبارت منظم و فلگ g/ چندین مورد مطابقت بازگشت میدهد:
let string = "Hello"; let ret = string.match(/l/g); // (2) [“l”، “l”];
میبینید که ما مطابقتهای چندگانه خود را با استفاده از نسخههای قبل از ES10 نیز به دست میآوریم و به خوبی کار میکند. بنابراین چرا باید خود را درگیر متد کاملاً جدید matchAll بکنیم؟ پیش از آن که بتوانیم به این سؤال با تفصیل پاسخ دهیم، باید به بررسی capture groups بپردازیم. این کار هیچ فایدهای هم که نداشته باشد، موجب میشود که با مفهوم جدیدی در مورد عبارتهای منظم آشنا شوید.
گروههای Capture در عبارت منظم
گروههای capture در regex صرفاً اقدم به استخراج یک الگو از پرانتزها میکنند. شما میتوانید گروههای مختلف را با (regex/.exec(string/ و با string.match، آنها را capture کنید. گروه capture منظم از طریق پوشش یک الگو در (pattern) قابل اجرا است؛ اما برای ایجاد یک مشخصه groups روی شیء بازگشتی باید از (name>pattern>?) استفاده کنیم.
برای ایجاد یک نام گروه جدید، کافی است <name>? را به ابتدای عبارت داخل پرانتز اضافه کنید و در نتیجه مطابقت گروهبندیشده (pattern) به صورت groups.name درمیآید که به شیء match الصاق یافته است. در ادامه میتوانید یک مثال عملی را مشاهده کنید.
نمونه رشتهای که باید مطابقت یابد:
در ادامه match.groups.color و match.groups.bird ایجاد میشوند:
متد regex.exec باید چندین بار فراخوانی شود تا کل مجموعه نتایج جستجو پیمایش شود. در طی هر تکرار زمانی که exec. فراخوانی میشود، نتیجه بعدی مشخص میشود. از این رو از حلقه While استفاده میشود.
خروجی کنسول:
اما یک نکته وجود دارد. اگر g/ را از regex حذف کنید، یک حلقه نامتناهی روی نتیجه نخست تا ابد ایجاد میشود. این وضعیت در گذشته مشکل بزرگی محسوب میشد. تصور کنید یک regex از یک پایگاه داده دریافت میکنید که مطمئن نیستید آیا g/ در انتهای خود دارد یا نه. بنابراین باید ابتدا این موضوع بررسی میشد.
اینک ما اطلاعات مقدماتی کافی برای پاسخ دادن به سؤال مطرح شده در بخش پیشین را داریم.
دلایل کافی برای استفاده از ()matchAll.
- این گزینه میتواند در زمان استفاده از capture groups عملکرد بهتری داشته باشد. یک capture group در واقع صرفاً بخشی از عبارت منظم با پرانتز () است که یک الگو را استخراج میکند.
- این گزینه به جای آرایه یک iterator بازگشت میدهد. Iterator-ها خود اشیای مفیدی هستند.
- Iterator میتواند با استفاده از عملگر spread یعنی (...) به یک آرایه تبدیل شود.
- با استفاده از این گزینه دیگر نیازی به عبارتهای منظم با g/ انتهایی وجود ندارد و بنابراین در مواردی که عبارت منظم از یک پایگاه داده یا منبع خارجی دریافت میشود و به همراه شیء RegEx مورد استفاده قرار میگیرد مناسب خواهد بود.
- عبارتهای منظم که با استفاده از شیء RegEx ایجاد میشوند، نمیتوانند با استفاده از عملگر نقطه (.) زنجیرهسازی شوند.
- و نکته پیشرفته آخر این که شیء RegEx مشخصه درونی lastIndex. را که به ردگیری آخرین موقعیت مطابقت یافته میپردازد تغییر میدهد. این وضعیت میتواند موجب بروز خرابی در موارد پیچیدهتر شود.
طرز کار ()matchAll. چگونه است؟
در ادامه طرز کار این متد را در حالتهای مختلف بررسی میکنیم.
حالت ساده
در این بخش تلاش میکنیم همه موارد وجود حروف e و i را در کلمه hello بیابیم. از آنجا که یک iterator بازگشت مییابد، میتوانیم این کار را با استفاده از یک حلقه for…of انجام دهیم:
شما میتوانید این بار از g/ استفاده نکنید، زیرا از سوی متد matchAll. لازم نیست. نتیجه کار چنین است:
نمونهای از گروههای capture با ()matchAll.
()matchAll. همه مزیتهای فهرست شده فوق را دارد. این یک iterator است و از این رو میتوانیم با استفاده از یک حلقه for…of آن را پیمایش کنیم. این همه تفاوت ساختاری آن را شامل میشود.
دقت کنید که g/ استفاده نشده است، زیرا از قبل از سوی ()matchAll. به کار گرفته شده است.
خروجی کنسول:
شاید از نظر زیباییشناسی شباهت زیادی به پیادهسازی حلقه regex.exec اصلی داشته باشد؛ اما همان طور که پیشتر اشاره کردیم، روش بهتری محسوب میشود که دلایل آن را قبلاً بیان کردیم. همچنین حذف g/ موجب بروز یک حلقه نامتناهی نخواهد شد.
ایمپورت دینامیک
ایمپورتها اینک میتوانند به یک متغیر انتساب یابند:
()Array.flat
مسطح سازی یک آرایه چندبعدی به صورت زیر است:
()Array.flatMap
این متد موجب میشود که:
به صورت زیر درآید:
و اگر این map مجدداً مسطح شود، به صورت زیر درمیآید:
()Object.fromEntries
این متد یک لیست از جفتهای کلید-مقدار را به یک شیء تبدیل میکند:
()String.trimStart و ()String.trimEnd
()JSON.stringify با ترکیب متناسب
این بهروزرسانی موجب اصلاح پردازش کاراکترهای U+D800 تا U+DFFF میشود که در برخی موارد ممکن است در رشتههای JSON حضورداشته باشند. این مسئله نمیتواند موجب بروز مشکلی شود، زیرا JSON.stringify میتواند این اعداد را به صورت اعدادی بازگشت دهد که به صورت مقادیری قالببندی شدهاند که معادل کاراکترهای UTF-8 نیستند. اما قالب JSON الزام میکند که از انکودینگ UTF-8 استفاده کنیم.
شیء JSON میتواند برای تجزیه قالب JSON استفاده شود. شیء جاوا اسکریپت JSON دارای متدهای stringify و parse است.
متد parse یا رشته JSON خوشتعریف به صورت زیر کار میکند:
دقت کنید که ما از گیومههای جفتی در پیرامون نامهای مشخصه استفاده کردیم و این وضعیت برای ایجاد یک رشته در قالب صحیح JSON مطلقاً ضروری است، عدم وجود گیومه یا استفاده از هر نوع دیگر از گیومه موجب میشود که یک JSON خوشتعریف نداشته باشیم.
قالب رشته JSON از Object Literal متفاوت است؛ گرچه تقریباً شبیه هم به نظر میرسند؛ اما نمیتوان از هیچ نوع گیومه پیرامون نام مشخصهها استفاده کرد و همچنین میتواند شامل متدهایی باشد که چنین چیزی در قالب رشته JSON مجاز نیست:
در هر حال، همه چیز درست به نظر میرسد. مثال نخست سازگار به نظر میرسد؛ اما مثالهای ساده دیگری نیز وجود دارند که در اغلب موارد بدون مشکل عمل میکنند.
کاراکترهای U+2028 و U+2029
مشکل اینجاست. EcmaScript تا پیش از نسخه ES10 در عمل به طور کامل از قالب JSON پشتیبانی نمیکرد. کاراکتر جداساز خط یعنی U+2028 در موردی که escape نشده بود و کاراکتر جداساز پاراگراف یعنی U+2029 تا قبل از نسخه ES10 پذیرفته نبودند:
U+2028 کاراکتر جداساز خط است
U+2029 کاراکتر جداساز پاراگراف است. در برخی موارد میتواند در رشته قالببندی شده JSON نیز ظاهر شود. همین نکته در مورد همه کاراکترهای بین U+D800 — U+DFFF نیز صدق میکند. اگر این کاراکترها در رشته قالببندی شده JSON ظاهر میشدند، ممکن بود ساعتها وقت شما صرف تلاش برای درک علت ایجاد خطا در جای دیگری از برنامه شود.
بنابراین اگر یک رشته مانند "('console.log(‘hello” را به eval ارسال کنید، با تبدیل کردن آن به کد واقعی، آن را به عنوان یک گزاره جاوا اسکریپت اجرا میکند. این وضعیت مشابه طرز کار JSON.parse برای پردازش رشته JSON است.
()Stable Array.prototype.sort
در پیادهسازی قبلی V8 از یک الگوریتم مرتبسازی سریع ناپایدار برای آرایههای شامل بیش از 10 آیتم استفاده شده است. یک الگوریتم مرتبسازی پایدار الگوریتمی است که وقتی دو شیء با کلیدهای مساوی و ترتیب یکسان ظاهر میشوند، خروجی را طوری مرتبسازی کند که این دو شیء در وضعیت ورودی قرار داشته باشند.
اما این وضعیت اینک در ES10 اصلاح شده است و یک مرتبسازی پایدار برای آرایه ارائه شده است:
خروجی کنسول (دقت کنید که آیتمها با ترتیب معکوس نمایش مییابند):
متد جدید ()Function.toString
تابعها شیء هستند و هر شیء یک متد ()toString. دارد، زیرا اساساً روی ()Object.prototype.toString قرار دارد. همه اشیا که شامل تابعها نیز میشود از طریق وراثت از کلاس مبتنی بر پروتوتایپ از آن به ارث رسیدهاند.
این بدان معنی است که ما هم اینک نیز متد ()funcion.toString را داریم؛ اما ES10 تلاش کرده است تا بازنمایی رشته را برای همه اشیا و تابعهای داخلی استانداردسازی کند. در ادامه نمونههای مختلفی از کاربردهای گوناگون برای روشنتر شدن موضوع ارائه شده است:
مثال کلاسیک:
خروجی کنسول (متن تابع در قالب رشته):
⇨ function () { console.log('Hello there.'); }
و در ادامه بقیه کاربردها را میبینید.
زمانی که مستقیماً از نام تابع ناشی میشود:
function parseInt() { [native code] }
با چارچوب محدود:
⇨ function () { [native code] }
شیء تابع با قابلیت فراخوانی درونی:
⇨ function Symbol() { [native code] }
تابع ایجاد شده به صورت دینامیک:
⇨ function anonymous() {}
تابع generator ایجاد شده به صورت دینامیک:
⇨ function* () { }
prototype.toString:
⇨ Function.prototype.toString requires that 'this' be a Function"
بدین ترتیب در میان موقعیتهای بسیار مختلف استانداردسازی شده است.
اتصال Catch اختیاری
در گذشته، بند Catch در یک گزاره try / catch به صورت یک متغیر «الزامی» (required) بود. گزاره try / catch به ما کمک میکند که خطاها را در سطح ترمینال تفسیر کنیم. جهت یادآوری به مثال زیر توجه کنید:
اما در برخی موارد متغیر error الزامی بدون استفاده باقی میماند:
هر کس که کد فوق را نوشته است، قصد داشته است از بند try با الزام به true بودن خارج شود. اما این اتفاق نمیافتد:
در ES10 اتصال خطای Catch اختیاری است. شما اینک میتوانید از متغیر خطا رد شوید:
در حال حاضر هیچ روشی مانند مثال قبل برای ارزیابی گزاره try وجود ندارد؛ اما زمانی که این گزاره مطرح شود، این بخش از مقاله را بهروزرسانی خواهیم کرد.
شیء globalThis استاندارد شده
This سراسری تا قبل از ES10 استاندارد نشده بود. شما در کد اجرایی خود آن را خودتان روی چند پلتفرم با نوشتن کد زیر استانداردسازی میکردید:
اما همین کد نیز در همه موارد کار نمیکرد. بنابراین ES10 شیء globalThis را معرفی کرده است که باید از حالا به بعد برای دسترسی به دامنه عمومی روی هر پلتفرمی مورد استفاده قرار گیرد:
Symbol.description
Description یک مشخصه فقط-خواندنی است که یک توصیف اختیاری از شیءهای symbol بازگشت میدهد.
دستور زبان Hashbang
Hashbang یا shebang که کاربران «یونیکس» (Unix) با آن آشنا هستند، به تعیین یک مفسر میپردازد که اقدام به اجرای کد جاوا اسکریپت میکند.
ES10 به استانداردسازی این shebang پرداخته است. ما قصد نداریم وارد جزییات آن شویم، زیرا از نظر فنی در واقع یک ویژگی زبان محسوب نمیشود؛ اما اساساً به متحد ساختن شیوه اجرای جاوا اسکریپت در سمت سرور میپردازد. در واقع ما به جای کد زیر:
$./index.js
در سیستمعامل شبه Unix از کد زیر استفاده میکنیم:
$ node index.js
کلاسهای ES10: اعضای خصوصی، استاتیک و عمومی
کاراکتر جدید ساختاری octothorpe (#) یا هشتگ اینک برای تعریف متغیرها، تابعها، getter-ها و setter-ها به صورت مستقیم درون دامنه بدنه کلاس همراه با سازنده و متدهای کلاس میپردازد.
در ادامه یک نمونه هر چند بیمعنی را مشاهده میکنید که تنها روی ساختار جدید متمرکز شده است:
اگر بخواهیم صادق باشیم، این وضعیت موجب میشود که خوانایی این زبان کمی دشوارتر شود؛ اما با این حال همچنان یک ویژگی جالب محسوب میشود زیرا شبیه کلاسهای ++C است.
دقت کنید که این ویژگی هنوز به طور کامل پیادهسازی نشده است.
نتیجهگیری
ES10 مجموعه ویژگیهای جدیدی دارد که تا به حال فرصت بررسی کامل در «محیط اجرایی» (production) را نیافتهاند. در این نوشته تلاش کردیم برخی از مهمترین ویژگیهای ES10 را معرفی کرده و مورد بررسی قرار دهیم. دقت داشته باشید که برخی از این ویژگیهای هنوز در مرحله تکمیل شدن هستند و ممکن است تا زمان انتشار رسمی دچار تغییراتی شوند.
اگر این نوشته برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای طراحی و توسعه پروژههای وب
- آموزش JavaScript ES6 (جاوا اسکریپت)
- مجموعه آموزشهای برنامه نویسی
- آموزش جاوا اسکریپت (JavaScript)
- جاوا اسکریپت چیست؟ — به زبان ساده
- تابعهای جاوا اسکریپت — راهنمای جامع
- آموزش جاوا اسکریپت — مجموعه مقالات جامع وبلاگ فرادرس
==