WiX/mag

ویکس مگ / مجله آموزشی ویکس سِوِن

آموزش جاوا اسکریپت Es13 - اکما اسکریپت 2022

آموزش کامل جاوا اسکریپت Es13 (اکما اسکریپت 2022)

پیمان باقری

پیمان باقری

برنامه نویس و متخصص وب

مقدمه

قبلا تو مقاله ی ” ورژن بندی اکما اسکریپت (جاوا اسکریپت) ” از مجله آموزشی ویکس سون، امکانات و فیچرهایی که تو Es13 به جاوا اسکریپت اضافه شده رو معرفی کردیم. در ادامه به جزییات این قابلیت ها و آموزششون میپردازیم.  

 

امکانات و قابلیت های جاوا اسکریپت Es13 (2022)

  1. قابلیت Top-Level await (استفاده از await، بیرون تابع async)
  2. متد at در داده های قابل پیمایش (آرایه، شبه آرایه و استرینگ ها)
  3. متد Object.hasOwn در آبجکت ها
  4. استفاده از cause در ساختار مدیریت خطای try-catch
  5. فلگ d/ در رجکس (RegExp)
  6. متد RegExp.prototype.exec در رجکس

 

آموزش کامل جاوا اسکریپت Es13 (2022)

(با توجه به اینکه این ورژن، ورژن نسبتا جدیدی به حساب میاد، با احتیاط از فیچرهاش استفاده کنید)

پیشنیاز این آموزش :

    1. آشنایی با مفاهیم پایه جاوا اسکریپت
    2. آشنایی با قابلیت های جدید جاوا اسکریپت تا اکما اسکریپت 2021 یا Es12

 

1) قابلیت Top-Level await (استفاده از await، بیرون تابع async) در جاوا اسکریپت Es13

تا قبل از جاوا اسکریپت Es13، نمیتونستیم از کلمه کلیدی await خارج یه تابع async استفاده کنیم. به همین خاطر بعضی مواقع مجبور میشدیم که از توابع خودخوان ناهمگام یا IIAFE استفاده کنیم و داخل این توابع از await استفاده کنیم. مثل کد زیر :

(async function () {
  await Promise.resolve(console.log("We Are WiX7")); // output: We Are WiX7
})();

اما تو این ورژن این امکان برامون فراهم شده که بتونیم خارج از توابع async، به سادگی از کلمه کلیدی await استفاده کنیم. اما نکته ای که مهمه اینه که این قابلیت فقط برای ماژول ها (بالای ماژول) فراهم شده، نه اسکریپت های کلاسیک. یعنی در واقع ماژول ها تو این زمینه مشابه توابع async عمل میکنن.

به عنوان مثال، فرض کنید ماژول اصلی ما App.mjs هست و یه ماژول فرزند به اسم Users.mjs داریم که لیست یوزرها رو از API، دریافت میکنه. همونطور که میدونید این عمل یه عمل async هست و میتونیم به کمک async-await این کار رو انجام بدیم، اما چون داریم از ماژول استفاده میکنیم نیازی به تعریف تابع async نیست و بالای ماژول به راحتی با استفاده از کلمه await لیست یوزرها رو fetch میکنیم. ببینید :

const response = await fetch("https://jsonplaceholder.typicode.com/users");
let users = await response.json();

export { users };

حالا، میتونیم تو ماژول اصلی یعنی App.mjs ، لیست یوزرها رو ایمپورت کنیم و بدون مشکل نمایششون بدیم :

import { users } from "./Users.mjs";

function render(users) {
  if (users) {
    const list = users
      .map((user) => {
        return `<li>${user.name}</li>`;
      })
      .join("");

    return `<ol>${list}</ol>`;
  } else {
    throw "The user list is not available";
  }
}

const container = document.querySelector(".container");
try {
  container.innerHTML = render(users);
} catch (e) {
  container.innerHTML = e;
}

توضیح کد بالا : لیست یوزرها رو از ماژول Users.mjs ایمپورت کردیم و بعد به کمک تابع render یه لیست از یوزرها ساختیم. المنتی که تو صفحه اچ تی ام ال کلاس container داره رو انتخاب کردیم و لیست یوزرها رو واردش کردیم که تو صفحه نمایش داده بشه.

نکته : اگه میخواستیم همین کار رو بدون قابلیت Top-Level await انجام بدیم، باید تو ماژول Users.mjs یه تابع IIAFE مینوشتیم و export default میکردیم و بعد تو ماژول اصلی یعنی App.mjs هم users رو ایمپورت میکردیم و هم promise، در نهایت هم باید با promise.then لیست رو وارد صفحه میکردیم (پیچیده شد؟)

به رفرنس : V8 / jstut

 

2) متد at در داده های قابل پیمایش (آرایه، شبه آرایه و استرینگ ها) جاوا اسکریپت Es13

همونطور که میدونید به کمک براکت [ ] میتونیم به المنت های آرایه مون دسترسی پیدا کنیم. مثلا برای دسترسی به المنت اول و دوم آرایه به ترتیب ایندکس 0 و 1 رو فراخوانی میکنیم :

arrayName[0];
arrayName[1];

حالا اگه بخوایم مثلا به آخرین ایندکس آرایه دسترسی داشته باشیم، میتونیم از دستور زیر استفاده کنیم :

arrayName[arrayName.length - 1]

اما مشکلی که هست اینه که نمیتونیم به همین روش ایندکس های منفی رو جستجو کنیم و اگه عدد منفی داخل براکت قرار بدیم، undefined برمیگردونه. دلیلشم اینه که از براکت تو جاوا اسکریپت برای سرچ داخل آبجکت ها هم استفاده میشه و وقتی عدد منفی (مثلا -1) داخل براکت قرار میدیم، جاوا اسکریپت به جای اینکه دنبال المنت مورد نظرمون تو آرایه بگرده، میره دنبال پراپرتی با کلید -1 داخل آبجکتی که هم نام با آرایه مونه میگرده و چون چنین چیزی وجود نداره undefined برمیگردونه. این شد که متد ()at متولد شد!

متد Array.prototype.at بهمون این قابلیت رو میده که بتونیم ایندکس های مثبت و منفی رو تو آرایه مون سرچ کنیم. ورودی این متد اگه :

    1. عدد مثبت باشه : شمارش از اول آرایه انجام میشه و at(0) اولین المنت رو برمیگردونه
    2. عدد منفی باشه : شمارش از آخر آرایه انجام میشه و at(-1) آخرین المنت رو برمیگردونه
const array1 = [1, 2, 3, 4, 5, 6];

console.log(array1.at(0)); // output: 1
console.log(array1.at(-1)); // output: 6

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

رفرنس : MDN / jstut

 

3) متد hasOwn در آبجکت های جاوا اسکریپت Es13

حتما با متد hasOwnProperty آشنا هستین. به کمک این متد میتونستیم تشخیص بدیم که آیا پراپرتی مورد نظرمون، داخل خود آبجکت هست یا اینکه از طریق ارث (پروتوتایپ) به آبجکت رسیده؟ کار متد hasOwn هم شبیه به همین متده و در واقع ارائه شده تا جایگزینی برای hasOwnPeoperty باشه و مشکلاتی که قبلا تو این متد وجود داشت برطرف کنه. سینتکس کلی متد hasOwn به این صورته :

Object.hasOwn(obj, 'prop'); // true or false
  • obj : اسم آبجکتی که میخوایم داخلش جستجو کنیم
  • prop : اسم پراپرتی که میخوایم بررسی کنیم آیا برای خود آبجکت هست یا نه

برای اینکه تفاوت سینتکس این 2 متد رو بهتر متوجه بشین به 2 مثال زیر توجه کنید :

برای استفاده از hasOwnProperty ، اول اسم آبجکت رو مینویسیم و بعد hasOwnProperty رو به عنوان متدش فراخوانی میکنیم و اسم پراپرتی مورد نظرمونو داخل متد مینویسیم :

const wixSeven = {
  name: "WiX7",
};

wixSeven.hasOwnProperty("name"); // true

برای استفاده از hasOwn ، نیاز به نوشتن اسم آبجکت اول دستور نیست. اول متد Object.hasOwn رو مینویسیم و بعد به عنوان پارامتر اولش، اسم آبجکت و به عنوان پارامتر دومش، اسم پراپرتی رو وارد میکنیم :

const wixSeven = {
  name: "WiX7",
};

Object.hasOwn(wixSeven, "name"); // true

نکته : هر دو متد اگه حتی مقدار (value) پراپرتی مورد نظرمون null یا undefined باشه، باز هم true برمیگردونن.

نکته : متد hasOwn توی آرایه ها هم کاربرد داره و میشه بررسی کرد که آیا آرایه، ایندکس مورد نظرمونو داره یا نه (سینتکسش همونه، فقط پارامتر، اول اسم آرایه و پارامتر دوم، شماره ایندکس مورد نظرمونه).

نکته : هر دو متد hasOwn و hasOwnProperty ، جستجو رو فقط داخل خود آبجکت انجام میدن (نه زنجیره پروتوتایپ)، و اگه بخوایم جستجو داخل زنجیره پروتوتایپ انجام بدیم باید از عملگر in استفاده کنیم.

رفرنس : MDN

 

4) استفاده از cause در ساختار مدیریت خطای try-catch در جاوا اسکریپت Es13

کلمه کلیدی cause در واقع یه پراپرتی جدید برای Error ها هستش که تو Es13 به جاوا اسکریپت اضافه شده و اطلاعات بیشتری در مورد خطایی که داخل ساختار try-catch رخ میده رو در اختیارمون قرار میده. به دو صورت میشه تو بلاک catch ازش استفاده کرد :

اگه بخوایم از throw new Error استفاده کنیم میتونیم به عنوان پارامتر دوم Error ارسالش کنیم. به این صورت:

try {
  fetch("https://fakeurl/api");
} catch (error) {
  throw new Error("custom error", { cause: error });
}

میتونیم به عنوان پراپرتیِ آبجکت error فراخوانیش کنیم. تو این حالت بهتره شرط وجودش رو هم چک کنیم. به این صورت:

try {
  fetch("https://fakeurl/api");
} catch (error) {
  if (error.cause) {
    console.error(`Error caused by: ${error.cause}`);
  } else {
    console.error(`Error: ${error}`);
  }
}

نکته : به رفرنس : MDN / V8

 

5) فلگ d/ در رجکس (RegExp) در جاوا اسکریپت Es13

فلگ جدید d/ یا به عبارتی d modifier اطلاعاتی در مورد ایندکس شروع و پایان “هر نتیجه ی یافت شده” تو متن رو بهمون میده. به عنوان مثال رجکس زیر رو در نظر بگیرین. تو این رجکس دنبال کلماتی هستیم که با wixseven شروع میشن و یه word character هم آخرشون هست (شامل a-z / A-Z / 0-9) :

let myString = "wixseven1 wixsevenA wixseven.";
let myRegEx = /wixseven(\w)/g;
let myResult = [...myString.matchAll(myRegEx)];
console.log(myResult);
/* output:
0: ['wixseven1', '1', index: 0, input: 'wixseven1 wixsevenA wixseven.', groups: undefined]
1: ['wixsevenA', 'A', index: 10, input: 'wixseven1 wixsevenA wixseven.', groups: undefined]
*/

نکته : w/ کاراکترهای عددی 0-9 و حروف a-z / A-Z رو میپذیره + کاراکتر آندرلاین (_) ، به این کاراکترها word character میگن.

همونطور که انتظار داشتیم. 2 مورد اول چون با رجکس تطابق داشتن، تو خروجی نمایش داده شدن. اما حالا میخوایم فلگ d/ رو به رجکس اضافه کنیم ببینیم چه تغییری رخ میده تو نتیجه :

let newString = "wixseven1 wixsevenA wixseven.";
let newRegEx = /wixseven(\w)/dg;
let newResult = [...myString.matchAll(newRegEx)];
console.log(newResult);
/* output:
0: ['wixseven1', '1', index: 0, input: 'wixseven1 wixsevenA wixseven.', groups: undefined, indices: Array(2)]
1: ['wixsevenA', 'A', index: 10, input: 'wixseven1 wixsevenA wixseven.', groups: undefined, indices: Array(2)]
*/

همونطور که میبینید indices به خروجی اضافه شد. حالا از همین indices مربوط به اولین کلمه خروجی میگیریم که نتیجه رو ببینیم :

console.log(newResult[0].indices);
/* output:
0: [0, 9]
1: [8, 9]
*/

عدد 9 تو هر دو نتیجه ی بالا، نقطه پایان یا End Point رو نشون میده (یک رقم از آخرین ایندکس کلمه بیشتره) و عدد 0 و 8 هم به ترتیب ایندکس های شروع و پایان کلمه رو بهمون نشون میدن. حالا دومین کلمه :

console.log(newResult[1].indices);
/* output:
0: [10, 19]
1: [18, 19]
*/

اینجا هم عدد 19، نقطه پایان و عدد 10 و 18 به ترتیب ایندکس های شروع و پایان کلمه رو بهمون نشون میدن.

رفرنس  dev.to

 

6) متد RegExp.prototype.exec در رجکس در جاوا اسکریپت Es13

متد exec (مخفف execute) شبیه به متد match عمل میکنه. به این صورت که داخل متن مورد نظر دنبال الگوی رجکسی که بهش میدیم مگیرده و اگه نتیجه ای وجود داشت در قالب یه آرایه برمیگردونه و اگه وجود نداشت null برمیگردونه.

اما تفاوتشون چیه؟ متد match برای استرینگ هاست (String.prototype.match) اما متد exec برای رجکس هست (RexExp.prototype.exec)، به همین خاطر به نوعی میشه گفت که از لحاظ سینتکس، برعکس هم نوشته میشن. به این صورت :

str.match(rgx)
rgx.exec(str)

همونطور که میبینید برای متد match، رجکس به عنوان ورودی ارسال میشه و برای exec، استرینگ به عنوان ورودی ارسال میشه.

اما تفاوتشون در عملکرد چیه؟ از اونجایی که exec متدی برای رجکس هست، هر بار که ازش استفاده کنیم، آبجکت RegExp (در واقع پراپرتی lastIndex رجکس) آپدیت میشه. در صورتی که متد match هیچ تاثیری روی آبجکت RegExp نمیذاره. به عنوان مثال تو کد زیر از متد exec استفاده کردیم و حلقه ای تعریف کردیم که هربار که یه نتیجه جدید یافت میشه، مقدار آپدیت شده ی lastIndex رو نمایش بده :

const myRegex = RegExp("wix*", "g");
const myString = "wixseven is a programming tutorial platform. follow us on wix7.com";
let myArray;

while ((myArray = myRegex.exec(myString)) !== null) {

  console.log(`${myArray[0]} was found. Next starts at ${myRegex.lastIndex}`);
  /* output:
  wix was found. Next starts at 3
  wix was found. Next starts at 61
  */
 
}

نکته : در صورتی که بخوایم متد exec ، بیشتر از 1 نتیجه رو برگردونه، حتما باید از فلگ g استفاده کنیم

رفرنس : MDN

 

جمع بندی

این ورژن از جاوا اسکریپت هم آپدیت های خوبی داشت. از قابلیت Top-Level await گرفته تا متدهای at و hasOwn و قابلیت های جدید رجکس، که نشون میده کماکان تیم اکما تمام تلاشش رو میکنه که این زبان پرکاربرد رو به روز و زنده نگه داره.

اگه ایرادی تو آموزش دیدین یا سوالی داشتین خوشحال میشیم تو قسمت نظرات مطرح کنید.

پیروز باشید.