قابلیت های مفید و کمتر شناخته شده جاوا اسکریپت — راهنمای کاربردی
در این مقاله قصد داریم به بررسی بیش از 11 قابلیت مفید جاوا اسکریپت بپردازیم که اکثر افراد حتی از وجود آنها اطلاع ندارند. توجه داشته باشید که برخی از این قابلیتها منسوخ شدهاند و استفاده از برخی از آنها نیز از سوی EcmaScript ممنوع شده است. با این حال این ویژگیها همچنان در کتابخانههای مختلف دیده میشوند و از این رو یادگیری آنها مفید خواهد بود. پیش از شروع ناگفته نماند که این مطلب صرفاً جنبه آموزشی دارد و انتخاب استفاده یا عدم استفاده از قابلیتهایی که در ادامه ذکر شدهاند بر عهده شما است.
1. لفظهای قالبی تگدار
1taggingThis`Hey!!, I'm ${name} !!`
اگر تاکنون از کامپوننتهای استایلدار استفاده کرده باشید، در واقع از «لفظهای قالبی تگدار» (Tagged Template Literals) استفاده کردهاید.
لفظهای قالبی تگدار کنترل بیشتری روی تحلیل لفظها در اختیار ما قرارمی دهند. بدین ترتیب میتوانیم لفظهای قالبی را با استفاده از یک تابع تحلیل کنیم.
1taggingThis`Hey!!, I'm ${name} !!`
taggingThis یک تابع است. آرگومان نخست یک تابع تگ شامل آرایهای از مقادیر رشتهای است و بقیه آرگومانها با عبارتها مرتبط هستند.
1function taggingThis(strings, ...vals) {
2 //...
3}
4taggingThis`Hey!!, I'm ${name} !!`
آرگومان strings حاوی مقدار زیر است:
1["Hey!!, I'm ", '!!']
در حالی که بقیه آرگومانها که به آرایه ارسال میشوند به صورت زیر هستند:
1vals['Nnamdi']
در این تابع taggingThis، میتوانیم رشته را دستکاری کنیم و چیز متفاوتی بازگشت دهیم.
1function taggingThis(strings, ...vals) {
2 return strings[0] + vals[0] + strings[1]
3}
4const name = "Nnamdi"
5taggingThis `Hey!!, I'm ${name} !!` // Hey!!, I'm Nnamdi !!
کد زیر مثالی از شیوه استفاده از آن را نمایش میدهد:
1import React from 'react';
2import styled, { keyframes } from 'styled-components'
3import PropTypes from 'prop-types';
4
5const appear = keyframes`
6 0% {
7 opacity: 0;
8 transform: translateY(-5px);
9 }
10 50% {
11 opacity: 0.2;
12 transform: translateY(0);
13 }
14 100% {
15 opacity: 1;
16 }
17`
18
19const Item = styled.li`
20 border-bottom: 1px #d1d1d1 solid;
21 border-right: 1px #d1d1d1 solid;
22 border-left: 1px #d1d1d1 solid;
23 &:first-child{
24 border-top: 1px #d1d1d1 solid;
25 border-top-left-radius: 5px;
26 border-top-right-radius: 5px;
27 }
28 &:last-child {
29 border-bottom-left-radius: 5px;
30 border-bottom-right-radius: 5px;
31 }
32 padding: 12px;
33 display: flex;
34 justify-content: space-between;
35 align-items: center;
36 animation: ___CSS_0___ 1s ease;
37`
38const Text = styled.span`
39 position: relative;
40 font-family: 'Caveat', cursive;
41 font-size: 24px;
42`
43const DelButton = styled.button`
44 font-size: 18px;
45 font-weight: 700;
46 color: #adadad;
47 padding: 5px;
48 background-color: transparent;
49 border: 0;
50 appearance: none;
51 outline: unset;
52 transition: transform .8s;
53 &:hover{
54 color: #c75656
55 transform: rotate(180deg);
56 }
57`
58
59const RemovableListItem = (props) => {
60 const {id, text, removeItem} = props;
61
62 return(
63 <Item>
64 <Text>{text}</Text>
65 { (removeItem && id) &&
66 <DelButton onClick={() => removeItem(id)}>X</DelButton>
67 }
68 </Item>
69 )
70}
71
72RemovableListItem.propTypes = {
73 id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
74 text: PropTypes.string.isRequired,
75 removeItem: PropTypes.func
76}
77
78
79
80export default RemovableListItem;
2. عملگر کاما
عملگر کاما عبارتها را از هم جدا میکند و آخرین عبارت را در یک زنجیره بازگشت میدهد:
1let expr = (99, 88, 77)
2expr // 77
ما سه عبارت اصلی داریم که شامل 99، 88 و 77 میشود. همه آنها ارزیابی میشوند و آخرین مورد به expr انتساب مییابد. به مثال زیر توجه کنید:
1for(let i = 0, ii = 1; i< 10; i++, ii--) { ... }
این قابلیت در مواردی مفید است که میخواهیم تابعهای کوتاه لامبدا بنویسیم:
1const lb = (a, b, arr) => (arr.push(a*b), a*b)
در کد فوق دو گزاره وجود دارد که اولی نتیجه ضرب را به آرایه arr میفرستد و دومی a را در b ضرب میکند. نتیجه دومی به فراخواننده بازگشت مییابد. استفاده از این عملگر همراه با عملگر سهتایی نیز مفید است و ساختاری شبیه به لامبدای کوتاه دارد که به جای گزارهها، عبارتها را میپذیرد.
3. گزاره with
ابتدا باید بگوییم که استفاده از گزاره with به طور کلی توصیه نمیشود، چون منسوخ شده است. در حالت strict استفاده از آن کلاً ممنوع است. مشخص شده است که این گزاره وقتی با بلوکها استفاده میشود برخی مشکلات عملکردی و امنیتی در زبان ایجاد میکند. With یک کلیدواژه در زبان جاوا اسکریپت است که برای بسط زنجیره دامنه گزاره-(ها) به صورت زیر مورد استفاده قرار میگیرد:
1with(expression)
2 statement
3// OR
4with(expression) {
5 statement
6 statement
7 ...
8}
در کد فوق expression ارزیابی شده و دامنه پیرامون آن ایجاد میشود. expression و دامنه والد گزاره with درون براکتها در دسترس هستند:
1const a = 45
2let b = 8
3var f = 909
4with(f) {
5 log(f, b, a) // 909 8 45
6}
With موجب میشود که f درون زنجیره دامنه محصور شود. f و متغیرهای دیگر که خارج از بلوک with اعلان شدهاند در بدنه بلوک with قابل مشاهده خواهند بود. توجه داشته باشید که متغیرهای let و const که با بلوک with اعلان شده باشند، تنها درون این بلوک قابل مشاهده هستند و خارج از بلوک with دیده نمیشوند.
1with(f) {
2 log(f, b, a) // 909 8 45
3 let withinF = 20
4}
5log(withinF) // undefined
تلاش برای دسترسی به withinF موجب صدور خطای ReferenceError میشود، زیرا متغیر withinF تنها در بلوک with حضور دارد. با این که قابلیتی جالب است اما توصیه شده از آن استفاده نکنیم.
4. کلیدواژه in
in یک کلیدواژه است که برای بررسی وجود یک مشخصه در یک شیء استفاده میشود. حتماً تاکنون از آن در حلقه for..in بدون دانستن این که خود in یک کلیدواژه است، استفاده کردهاید. in در صورتی که مشخصه روی شیء وجود داشته باشد مقدار true بازگشت میدهد و در غیر این صورت مقدار بازگشتی false خواهد بود.
1const o = {
2 prop: 90
3}
4log("prop" in o) // true
در کد فوق چنان که میبینید in به صورت مستقل و بدون حلقه for…in استفاده شده است. این کلیدواژه بررسی می کند که آیا prop در شیء o وجود دارد یا نه. در این کد مقدار true بازگشت مییابد، زیرا prop در o تعریف شده است. اگر آن را روی مشخصه تعریف نشدهای بررسی کنیم:
1const o = {
2 prop: 90
3}
4log("prop1" in o) // false
مقدار false بازگشت مییابد. زیرا مشخصه prop1 روی شیء o وجود ندارد.
5. سازنده آرایه
آیا میدانید که میتوانید یک آرایه را بدون متد سنتی تعریف کنید؟
1const arr = [90,88,77]
این کار از طریق استفاده از سازنده آرایه ممکن است:
1const arr = new Array(90, 88, 77)
چیدمان پارامترهای ارسالی به سازنده، پایهای برای اندیسهای آن تشکیل میدهد. بدین ترتیب در مثال فوق مقدار 90 دارای اندیس نخست (0) است، 88 اندیس 1 و 77 نیز اندیس 2 را میگیرد.
arr[0] // 90 arr[1] // 88 arr[2] // 77
استفاده از (...)new Array مانند یک لفظ آرایهای است، بنابراین:
1const arr = new Array(90, 88, 77)
در واقع همان کار کد زیر را انجام میدهد:
1const arr = [90, 88, 77]
برخی افراد فکر میکنند که استفاده از قابلیت سازنده آرایه یک عیب یا ناسازگاری محسوب میشود. ایجاد یک آرایه به روش فوق با استفاده از new و سازنده آرایه زمانی ممکن است که تعداد آرگومان ارسالی به ()new Array بزرگتر یا مساوی 2 باشد. بنابراین نوشتن کدی مانند زیر:
1const arr = new Array(90, 88, 77)
موجب ایجاد آرایه [90, 88, 77] میشود. دلیل این امر آن است که آرگومانهای ارسالی به new Array(..) برابر با 3 هستند که بزرگتر یا مساوی 2 است. ارسال یک آرگومان به new Array(...) موجب میشود که موتور جاوا اسکریپت فضایی برای یک آرایه با اندازه ارسالی تخصیص دهد.
1const arr = new Array(6)
بدین ترتیب آرایهای با 6 آیتم یا عنصر ایجاد میشود و طول آن 6 خواهد بود:
1const arr = new Array(6)
2arr // [<6 empty items>]
3arr.length // 6
بنابراین دو کد زیر عملکردی شبیه هم دارند:
1const arr = new Array(6)
1const arr = [, , , , , ]
2arr // [ <6 empty items> ]
3arr.length // 6
این دلیلی بر ناسازگاری یا عجیب بودن این قابلیت نیست. این قابلیت در مشخصات EcmaScript نیز آمده است:
بنابراین یک ناسازگاری محسوب نمیشود، چون در مشخصات زبان جاوا اسکریپت مورد اشاره قرار گرفته است. مطالعه مشخصات یک زبان پیش از هر گونه نتیجهگیری ایده خوبی محسوب میشود.
6. سازنده تابع
آیا میدانید که میتوانید یک تابع را با استفاده از سازنده تابع تعریف کنید؟ ما در جاوا اسکریپت تابعها را عموماً به صورت زیر تعریف میکنیم:
1const mul = (a, b) => a * b
2
3// OR
4
5function mul(a, b) {
6 return a * b
7}
8
9// OR
10
11const mul = function(a, b) {
12 return a * b
13}
اما میتوانیم این کار را با استفاده از سازنده تابع نیز انجام دهیم:
1const mul = new Function("a", "b", "return a * b")
پارامترهای ارسالی به این تابع، آرگومانهای بدنه تابع را تشکیل میدهند. متغیر mul نیز نام تابع خواهد بود. توجه کنید که پارامتر آخر بدنه تابع است، در حالی که پارامترهای قبل از آن، آرگومانهای تابع هستند. مثلاً در مورد mul، پارامترهای a و b در واقع پارامترهای تابع هستند و پارامتر return a * b بدنه تابع را تشکیل میدهد. بدنه تابع دو پارامتر a و b را در هم ضرب کرده و نتیجه را بازگشت میدهد:
1const mul = new Function("a", "b", "return a * b")
2log(mul(7, 8)) // 56
بر اساس مستندات MDN:
فراخوانی مستقیم یک سازنده میتواند باعث ایجاد تابعها به صورت دینامیک شود، اما از مشکلات امنیتی و عملکردی در مورد eval رنج میبرد. با این حال برخلاف eval سازنده تابع، تابعهایی میسازد که صرفاً در دامنه سراسری اجرا میشوند.
7. تخریب آرایه
عناصر یک آرایه را میتوانیم با استفاده از شماره اندیس عنصر تخریب کنیم:
88
عناصر 99، 88 و 77 دارای اندیسهای 0، 1 و 2 هستند. برای دریافت عنصر 99 از آرایه arr باید آن را به صورت یک مشخصه به آرایه ارسال کنیم:
arr[1]
این کاری است که در مورد اشیا نیز انجام میدهید:
1let o = {
2 prop: 99
3}
4o[prop] // 99
بدن ترتیب میتوانیم prop را به روش زیر از o حذف کنیم:
1const {prop} = o
2
3prop // 99
همین کار را در مورد آرایهها نیز میتوان انجام داد:
1const arr = [99, 88, 77]
2
3const { 1: secA } = arr
4secA // 88
5
6const { 0: firstA, 1: secA, 2: thirdA } = arr
7
8firstA // 99
9secA // 88
10thirdA // 77
چنان که میبینید در کد فوق از شماره اندیس برای استخراج عناصر استفاده کردهایم. اندیسها مشخصههایی هستند که موقعیت عناصر را در آرایه تعریف میکنند. بنابراین کد زیر:
1const arr = {
2 0: 99,
3 1: 88,
4 2: 77,
5 length: 3
6}
آرایهها نیز شیء هستند و از اینرو تخریب شیء روی آنها کار میکند، اما یک ساختار خاص تخریب آرایه به صورت زیر نیز وجود دارد:
1const [first, second, third] = arr;
بدن ترتیب باید اطلاعات موقعیتی خاص ارائه شامل اندیس آغاز، پایان و غیره را بدانید.
8. کاهش محتوای آرایه با مشخصه length
آرایهها را میتوان با استفاده از دستکاری مشخصه length مورد کاهش قرار داد. مشخصه length در آرایه نشان دهنده تعداد عناصر آن است.
1const arr = [99, 88, 77]
2arr.length // 3
کاهش مشخصه length موجب میشود که موتور جاوا اسکریپت تعداد عناصر را تا رسیدن به مقدار مشخصه length کاهش دهد:
1const arr = [99, 88, 77]
2arr.length // 3
3arr.length = 1
4arr // [99]
مشخصه length آرایه arr برابر با 1 تعیین شده است و از این رو دو عنصر از سمت راست آرایه حذف شدهاند تا برابر با مقادیر مشخصه length شود. اگر مقدار مشخصه length افزایش یابد، موتور جاوا اسکریپت عناصر (تعریف نشده) به آرایه اضافه میکند تا تعداد عناصر در آرایه برابر با مقدار کل مشخصه length شود.
1const arr = [99, 88, 77]
2arr.length // 3
3arr.length = 1
4arr // [ 99 ]
5
6arr.length = 5
7arr // [ 90, <4 empty items> ]
عناصر موجود در arr ابتدا برابر با یک بود و سپس طول آن (length) به 5 افزایش مییابد و از این رو 4 آیتم دیگر اضافه میشود تا محتوای آن برابر با 5 شود.
9. Arguments
ما از کلیدواژه arguments برای دریافت آرگومانهای ارسالی به یک تابع استفاده میکنیم. نکته جالب در مورد arguments این است که میتوانیم آرگومانهای ارسالی به تابع با شیء arguments را بدون تعریف صریح متغیر arguments در تابع به دست آوریم:
1function myFunc() {
2 arguments[0] // 34
3 arguments[1] // 89
4}
5
6myFunc(34,89)
شیء arguments دارای اندیس آرایهای است. یعنی مشخصهها عدد هستند و از این رو میتوان از طریق ارجاع به کلید به آنها دسترسی یافت. شیء arguments از کلاس Arguments وهلهسازی میشود که برخی مشخصههای جالبی دارد. arguments.callee.name به نام تابعی که هم اینک فراخوانی شده است، اشاره میکند.
1function myFunc() {
2 log(arguments.callee.name) // myFunc
3}
4
5myFunc(34, 89)
arguments.callee.caller.name به نام تابعی اشاره میکند که تابع اجرا شده کنونی را فراخوانی کرده است:
1function myFunc() {
2 log(arguments.callee.name) // myFunc
3 log(arguments.callee.caller.name) // myFuncCaller
4}
5
6(function myFuncCallee() {
7 myFuncCaller(34, 89)
8})()
این قابلیت به طور خاص در تابعهای variadic مفید است.
10. حذف براکتها
آیا میدانید که در زمان وهله سازی از یک شیء میتوان براکتها را حذف کرد؟ به مثال زیر توجه کنید:
1class D {
2 logger() {
3 log("D")
4 }
5}
6
7// Normally, we do this:
8(new D()).logger() // D
9
10// Also, we can skip the ():
11(new D).logger() // D
12// and it still works
براکتها اختیاری هستند و این موضوع حتی در مورد کلاسهای داخلی نیز صادق است:
1(new Date).getDay()
2(new Date).getMonth()
3(new Date).getYear()
11. عملگر Void
void کلیدواژهای در جاوا اسکریپت است که یک گزاره را ارزیابی کرده و مقدار undefined بازگشت میدهد. به مثال زیر توجه کنید:
1 logger() {
2 return 89
3 }
4}
5
6const d = new D
7
8log(void d.logger()) // undefined
چنان که میبینید متد لاگر باید مقدار 89 بازگشت دهد، اما کلیدواژه void آن را تهی میکند و به جای آن مقدار تعریف نشده بازگشت میدهد. عملگر void بدینجهت استفاده میشود که مطمئن شویم یک مقدار تعریف نشده واقعی به دست میآوریم. همچنین به منظور کاهش کدنویسی مورد استفاده قرار میگیرد.
12. مشخصه تابع
به صورت زیر میتوان مشخصههایی در تابعها تعیین کرد:
1function func() {
2 func.prop1 = "prop1"
3}
4func.prop2 = "prop2"
تابعها شیء هستند و از این رو در کد فوق عمل مُجازی انجام مییابد.
این مشخصه تابع یک مشخصه استاتیک روی همه وهلههای تابع در زمان نیاز به یک شیء است.
1const j1 = new func()
2const j2 = new func()
3j1.prop1 // prop1
4j2.prop1 // prop1
5j1.prop1 = "prop1 changed"
6j2.prop1 // prop1 changed
همچنین میتواند در صورت استفاده به عنوان یک تابع، یک مشخصه سراسری باشد:
1function func() {
2 func.prop1 === undefined ? func.prop1 = "yes" : null
3 if(func.prop1 === "yes")
4 log("Prop with Yes")
5 if (func.prop1 == "no")
6 log("Prop with No")
7}
8func() // Prop with Yes
9func.prop1 = "no"
10func() // Prop with No
13. وراثت از طریق __proto__
__proto__ روشی برای وراثت مشخصهها از یک شیء در جاوا اسکریپت است. __proto__ یک مشخصه Object.prototype و یک مشخصه accessor است که [[Prototype]] یک شیء را افشا میکند و بدین ترتیب آن شیء قابل دسترسی میشود.
این __proto__ همه مشخصههای یک شیء را در [[Prototype]] به شیء هدف تعیین میکند. به مثال زیر توجه کنید:
1// proto.js
2const log = console.log
3const obj = {
4 method: function() {
5 log("method in obj")
6 }
7}
8const obj2 = {}
9obj2.__proto__ = obj
10obj2.method()
ما دو لفظ قالبی داریم که obj و obj2 هستند. obj یک مشخصه متد method دارد. obj2 یک لفظ شیء خالی دارد یعنی هیچ مشخصهای برای آن تعیین نشده است. در ادامه به __proto__ مربوط به obj2 دسترسی یافته و آن را روی obj تعیین میکنیم. بدین ترتیب همه مشخصههای obj از طریق Object.prototype در اختیار obj2 قرار میگیرند. به همین جهت است که میتوانیم method را روی obj2 بدون دریافت خطا فراخوانی کنیم، در حالی که در آنجا تعریف نشده است.
$ node proto method in obj
obj2 مشخصههای obj را به ارث میبرد و از این رو مشخصه متد method در مشخصههای آن قابل دسترسی است. Proto روی شیءها برای مثال object literal ،Object ،Array ،Function ،Date ،RegEx ،Number ،Boolean ،String مورد استفاده قرار میگیرد.
14. عملگر یگانه +
عملگر یگانه + عملوندهای خود را به نوع Number تبدیل میکند.
1+"23" // 23
2+{} // NaN
3+null // 0
4+undefined // NaN
5+{ valueOf: () => 67 } // 67
6+"nnamdi45" // NaN
این عملگر در مواردی مفید است که بخواهیم تبدیل سریع متغیرها به Number داشته باشیم.
15. عملگر یگانه –
عملگر یگانه – عملوندهای خود را به نوع Number تبدیل کرده و سپس آنها را منفی میکند. این عملگر نتیجه عملگر یگانه + را معکوس میسازد. یعنی ابتدا عملوند خود را به مقدار Number تبدیل کرده و سپس مقدار آن منفی میکند.
1-"23" // -23
در کد فوق ابتدا رشته 23 به نوع عددی تبدیل میشود که عدد 23 به دست میآید. سپس این عدد مثبت منفی میشود و عدد 23- به دست میآید.
1-{} // NaN
2-null // -0
3-undefined // NaN
4-{ valueOf: () => 67 } // -67
5-"nnamdi45" // NaN
اگر نتیجه تبدیل به عدد مقدار NaN باشد، منفی سازی اعمال نمیشود. منفی سازی 0+ موجب تولید 0- و منفی سازی 0- موجب تولید 0+ میشود:
- +0 // -0 - -0 // 0
16. عملگر نمایی **
از این عملگر برای یافتن نمای یک عدد استفاده میشود. عملکرد آن مانند یافتن توان یک عدد در یک نمای خاص است. در ریاضیات اگر 2 به توان 3 برسد، یعنی باید سه تا 2 را در هم ضرب کنیم:
2 * 2 * 2
همان کار را در جاوا اسکریپت با استفاده از عملگر ** انجام میدهیم:
2 ** 3 // 8 9 ** 3 // 729
سخن پایانی
جاوا اسکریپت قابلیتهای جالب زیادی دارد. همچنان که در ابتدای این مقاله اشاره کردیم، هدف از ارائه این قابلیتها، صرفاً آگاهی یافتن خواننده از وجود آنها است و از آنجا که در برخی موارد این عملگرها منسوخ شدهاند و یا از سوی استانداردهای جدید ممنوع اعلام شدهاند، در مورد کاربرد آنها باید با دقت رفتار کنید.
اگر این مطلب برای شما مفید بوده، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامه نویسی
- آموزش جاوا اسکریپت (JavaScript)
- ۱۱ ترفند بسیار کاربردی جاوا اسکریپت — به زبان ساده
- پایتون یا جاوا اسکریپت کدام بهتر است؟ — راهنمای جامع
==