آخرین نسخه استاندارد زبان جاوااسکریپت نسخه چهاردهم ECMAScript 2023 است. این به روز رسانی شامل متدهای جدیدی در array prototype میباشد. در این مقاله قصد داریم تا تمام ویژگیهای این متدهای جدید، از جمله رفتار آنها با آرایههای sparse و آبجکتهای شبه آرایه را باهم بررسی کنیم.
یک موضوع مشترکی که بین متدهای جدید array prototype وجود دارد، تمرکز بر روی عدم تغییر آرایه اصلی، و return کردن یک آرایه کاملاً جدید است.
به طور کلی، همانطور که این چهار متد جدید نشان میدهد، عدم تغییر دادهها مزایای زیادی دارد. این مزایا فقط محدود به آرایهها نمیشود، بلکه تمام آبجکتهای جاوااسکریپت را هم دربرمیگیرد. در ادامه برخی از مهمترین مزایا را بررسی میکنیم:
متد toReversed()
مشابه متد reverse()
است، اما یک تمایز قابل توجه وجود دارد. toReversed()
عناصر موجود در یک آرایه را بدون mutation در آرایه اصلی معکوس میکند. آرایه fruits
زیر را در نظر بگیرید:
const fruits = ["apple", "orange", "banana"]
حالا fruits
را با استفاده از متد reverse()
معکوس میکنیم:
// Reverse the array const result = fruits.reverse() console.log(result) // ['banana', 'orange', 'apple'] console.log(fruits) // ['banana', 'orange', 'apple'] // original array is mutated
هنگام استفاده از متد reverse()
، آرایه اصلی دچار mutation میشود.
برای معکوس کردن آرایه بدون mutation، میتوانیم از متد toReversed()
استفاده کنیم.
// Reverse the array const result = fruits.toReversed() console.log(result) // ['banana', 'orange', 'apple'] console.log(fruits) // ["apple", "orange", "banana"] // original array is preserved
آرایههای sparse آرایههایی بدون عناصر متوالی هستند. برای مثال موارد زیر را در نظر بگیرید:
const numbers = [1,2,3] // Assign an item to index 11 numbers[11] = 12 console.log(numbers) // [۱, ۲, ۳, empty × ۸, ۱۲]
در مثال بالا، numbers
دارای هشت slot خالی است. numbers
یک آرایه sparse میباشد. اکنون به متد Reversed()
برمیگردیم. این متد چگونه با آرایههای sparse کار میکند؟
toReversed()
هرگز یک آرایه sparse را return نمیکند. اگر آرایه اصلی دارای slotهای خالی بود، آنها به صورت undefined
برمیگرداند.
فراخوانی متد toReversed()
را در آرایه numbers
زیر در نظر بگیرید:
const numbers = [1,2,3] // Assign an item to index 11 numbers[11] = 12 numbers.toReversed() // [۱۲, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 3, 2, 1]
همانطور که انتظار داشتیم، تمام slotهای خالی به عنوان مقادیر آیتم آرایه undefined
برگردانده میشوند.
حتی اگر متد toReversed()
به طور خاص در prototype Array
وجود داشته باشد، ممکن است در آبجکتهای شبه آرایه نیز فراخوانی شود.
یک آبجکت شبه آرایه معمولا دارای یک ویژگی length
، و به صورت اختیاری دارای ویژگیهایی با نام ایندکسهای عدد صحیح میباشد. آبجکتهای String نمونهای از آبجکتهای شبه آرایه هستند.
تابع toReversed()
ابتدا ویژگی length
آبجکتی را که بر روی آن فراخوانی شده است میخواند و سپس از طریق keyهای عدد صحیح آبجکت، از انتها تا ابتدا تکرار میشود که به معنی length - 1
تا ۰
است. این تابع مقدار هر ویژگی را به انتهای یک آرایه جدید اضافه کرده سپس آن را return میکند.
اکنون میخواهیم این موضوع را امتحان کنیم. کاربرد اشتباه toReversed()
روی یک رشته را در نظر بگیرید:
const s = "Ohans Emmanuel" // call `toReversed` directly on the string s.toReversed() //Uncaught TypeError: s.toReversed is not a function
حتی اگر یک آبجکت string یک آبجکت شبه آرایه باشد، این کدی که نوشیم اشتباه است. ما نمیتوانیم آن را به شیوه string.toReversed()
فراخوانی کنیم زیرا toReversed
در prototype string
وجود ندارد.
با این حال، ممکن است از متد call()
مانند شکل زیر استفاده کنیم:
const s = "Ohans Emmanuel" // Array.prototype.toReversed.call(arrayLike) Array.prototype.toReversed.call(s) //['l', 'e', 'u', 'n', 'a', 'm', 'm', 'E', ' ', 's', 'n', 'a', 'h', 'O']
این موضوع درمورد یک آبجکت شبه آرایه self-constructed چگونه عمل میکند؟ به مثال زیر توجه کنید:
// Has a length property and integer index property. const arrayLike = { length: 5, ۲: "Item #2" }
اگر این یک آرایه استاندارد بود، یک آرایه sparse میشد، یعنی طول آرایه ۵ است و در ایندکس شماره ۲ مقدار Item #2
وجود دارد.
نتیجه فراخوانی toReversed
را در این مثال در نظر بگیرید:
console.log(Array.prototype.toReversed.call(arrayLike)) // [undefined, undefined, 'Item #2', undefined, undefined]
تابع toReversed()
یک آرایه معکوس بدون ایجاد یک آرایه sparse تولید میکند. همانطور که انتظار داشتیم، slotهای خالی به صورت undefined
برگردانده میشوند.
.toSorted()
همتای متد .sort()
است. همانطور که ممکن است حدس زده باشیم، متد .toSorted()
برخلاف.sort()
، آرایه اصلی را تغییر نخواهد داد. عملیات مرتبسازی با استفاده از متد .sort()
را در نظر بگیرید:
const list = [1, 5, 6, 3, 7, 8, 3, 7] //Sort in ascending order const result = list.sort() console.log(result) // [۱, ۳, ۳, ۵, ۶, ۷, ۷, ۸] console.log(list) // [۱, ۳, ۳, ۵, ۶, ۷, ۷, ۸]
همانطور که در مثال بالا میبینیم، متد sort()
آرایه را در جای خود مرتب کرده و در نتیجه آرایه را دچار mutation میکند. اکنون نتیجه را با متد toSorted()
بررسی میکنیم:
const list = [1, 5, 6, 3, 7, 8, 3, 7] // Sort in ascending order const result = list.toSorted() console.log(result) // [۱, ۳, ۳, ۵, ۶, ۷, ۷, ۸] console.log(list) // [۱, ۵, ۶, ۳, ۷, ۸, ۳, ۷]
همانطور که میبینیم، toSorted()
یک آرایه جدید با عناصر مرتب شده return میکند.
باید به این نکته توجه داشته باشیم که toSorted()
همان سینتکس sort()
را حفظ میکند. برای مثال، ممکن است تابعی را تعیین کنیم که ترتیب مرتبسازی را مشخص میکند، به عنوان مثال، list.toSorted(compareFn)
.
به مثال زیر توجه کنید:
const list = [1, 5, 6, 3, 7, 8, 3, 7] //Sort the array in descending order list.toSorted((a,b) => a < b ? 1 : -1) // [۸, ۷, ۷, ۶, ۵, ۳, ۳, ۱]
slotهای خالی همیشه به صورت undefined
برگردانده میشوند. در واقع به گونهای با آنها رفتار میشود که گویی دارای مقدار undefined
هستند. با این حال، compareFn
برای این slotها فراخوانی نمیشود و همیشه در انتهای آرایه بازگشتی قرار میگیرند.
مثال زیر را برای یک آرایه که اولین slot آن خالی است در نظر میگیریم:
// Note the empty initial slot const fruits = [, "apple", "orange", "banana"] console.log(fruits.toSorted()) // ['orange', 'banana', 'apple', undefined]
این رفتار با زمانی که مقدار اولیه undefined
باشد، یکسان است. به عنوان مثال:
const fruits = [undefined, "apple", "orange", "banana"] console.log(fruits.toSorted()) // ['orange', 'banana', 'apple', undefined]
همچنین باید به این موضوع توجه داشته باشیم که slotهای خالی (یا slotهای undefined
) بدون توجه به موقعیت آنها در آرایه اصلی، همیشه به انتهای آرایه بازگشتی منتقل میشوند. مثلا:
// empty slot is in index 2 const fruits = ["apple", "orange", , "banana"] console.log(fruits.toSorted()) // returned last // ['orange', 'banana', 'apple', undefined] // undefined value is in index 2 const otherFruits = ["apple", "orange", undefined , "banana"] console.log(otherFruits.toSorted()) // returned last // ['orange', 'banana', 'apple', undefined]
هنگامی که همراه با آبجکتها از تابع toSorted()
استفاده میکنیم، ابتدا ویژگی length
آبجکت this
را میخواند. سپس keyهای عدد صحیح آبجکت را از ابتدا تا انتها جمعآوری میکند، یعنی از ۰
تا length - 1
. پس از مرتبسازی آنها، مقادیر مربوطه را در یک آرایه جدید return میکند. به عنوان مثال:
const s = "Ohans Emmanuel" // Array.prototype.toSorted.call(arrayLike) Array.prototype.toSorted.call(s) (۱۴) [' ', 'E', 'O', 'a', 'a', 'e', 'h', 'l', 'm', 'm', 'n', 'n', 's', 'u']
همینطور مثال زیر که با یک آبجکت شبه آرایه ساخته شده است:
// Has a length property and integer index property. const arrayLike = { length: 5, ۲: "Item #2" ۱۰: "Out of bound Item" // This will be ignored since the length is 5 } console.log(Array.prototype.toSorted.call(arrayLike)) // ['Item #2', undefined, undefined, undefined, undefined]
متد .toSpliced()
یکی دیگر از متدهای جدید array prototype است که همتای متد .splice()
میباشد. همانند روشهایی که درمورد آنها صحبت کردیم،toSpliced()
برخلاف .splice()
آرایهای را که فراخوانی شده است تغییر نمیدهد. همینطور سینتکس متد toSpliced
با .splice
یکسان است. مثلا:
toSpliced(start) toSpliced(start, deleteCount) toSpliced(start, deleteCount, item1) toSpliced(start, deleteCount, item1, item2, itemN)
همانطور که در مثال زیر داریم، با استفاده از متد .splice()
یک آیتم جدید به آرایه اضافه میکنیم:
const months = ["Feb", "Mar", "Apr", "May"] // Insert item "Jan" at index 0 and delete 0 items months.splice(0, 0, "Jan") console.log(months) // ['Jan', 'Feb', 'Mar', 'Apr', 'May']
متد splice()
آیتم جدید آرایه را وارد کرده و آرایه اصلی را دچار mutation میکند. برای ایجاد یک آرایه جدید بدون ایجاد mutation در آرایه اصلی، باید از متد toSpliced()
استفاده کنیم.
این بار مثال بالا را با استفاده از toSpliced()
بازنویسی میکنیم:
const months = ["Feb", "Mar", "Apr", "May"] // Insert item "Jan" at index 0 and delete 0 items const updatedMonths = months.toSpliced(0, 0, "Jan") console.log(updatedMonths) // ['Jan', 'Feb', 'Mar', 'Apr', 'May'] console.log(months) // ['Feb', 'Mar', 'Apr', 'May']
toSpliced()
یک آرایه جدید بدون mutation در آرایه اصلی return میکند. باید به این موضوع توجه داشته باشیم که سینتکس برای toSpliced()
و splice()
یکسان است.
toSpliced()
هرگز یک آرایه sparse را return نمیکند. به این ترتیب، slotهای خالی به صورت undefined
برگردانده میشوند. به عنوان مثال:
const arr = ["Mon", , "Wed", "Thur", , "Sat"]; // Start at index 1, and delete 2 items console.log(arr.toSpliced(1, 2)); // ['Mon', 'Thur', undefined, 'Sat']
هنگام کار با آبجکتهای شبه آرایه، متد toSpliced
طول آبجکت this
را میگیرد، key عدد صحیح مورد نیاز را میخواند و نتیجه را در یک آرایه جدید مینویسد:
const s = "Ohans Emmanuel" // Start at index 0, delete 1 item, insert the other items console.log(Array.prototype.toSpliced.call(s, 0, 1, 2, 3)); // [۲, ۳, 'h', 'a', 'n', 's', ' ', 'E', 'm', 'm', 'a', 'n', 'u', 'e', 'l']
متد آرایه .with()
بسیار جالب است. ابتدا، نماد براکت را برای تغییر مقدار یک ایندکس خاص از آرایه در نظر میگیریم:
const favorites = ["Dogs", "Cats"] favorites[0] = "Lions" console.log(favorites) //(۲) ['Lions', 'Cats']
با نماد براکت، آرایه اصلی همیشه دچار mutation میشود. متد .with()
همان عمل درج یک عنصر در یک ایندکس خاص را انجام میدهد، اما آرایه را تغییر نمیدهد. در عوض، آرایه جدیدی را با ایندکس جایگزین شده return میکند.
مثالی که داشتیم را با استفاده از .with()
بازنویسی میکنیم:
const favorites = ["Dogs", "Cats"] const result = favorites.with(0, "Lions") console.log(result) // ['Lions', 'Cats'] console.log(favorites) // ["Dogs", "Cats"]
with()
هرگز یک آرایه sparse را return نمیکند. به این ترتیب، slotهای خالی به صورت undefined
برگردانده میشوند. به عنوان مثال:
const arr = ["Mon", , "Wed", "Thur", , "Sat"]; arr.with(0, 2) // [۲, undefined, 'Wed', 'Thur', undefined, 'Sat']
مشابه سایر متدها، with()
ویژگی length
آبجکت this
را دریافت میکند. پس هر ایندکس صحیح مثبت (کمتر از length
) آبجکت را میخواند. با دسترسی به به این موارد، مقادیر ویژگی آنها را در ایندکس آرایه بازگشتی ذخیره میکند. در نهایت، index
و value
در signature فراخوانی with(index, value)
بر روی آرایه بازگشتی تنظیم میشود. مثلا:
const s = "Ohans Emmanuel" // Set the value of the first item console.log(Array.prototype.with.call(s, 0, "F")); // ['F', 'h', 'a', 'n', 's', ' ', 'E', 'm', 'm', 'a', 'n', 'u', 'e', 'l']
استاندارد ECMAScript مدام در حال بهبود است و استفاده از ویژگیهای جدید آن میتواند ایده بسیار خوبی باشد. در این مقاله سعی کردیم تا با متدهای جدید array prototype که عبارتند از: toReversed
، toSorted
، toSpliced
و with
بیشتر آشنا شویم تا بتوانیم با استفاده از آنها کدهای جاوااسکریپتی واضحتر بنویسیم.