'.?' Optional chaining
optional chainingیک افزوده اخیر به زبان برنامه نویسی جاوا اسکریپت است. مرورگرهای قدیمی ممکن است به polyfill نیاز داشته باشند.
optional chaining یک راه امن برای دسترسی به ویژگی های تودرتو شیء است، حتی اگر یک ویژگی میانی وجود نداشته باشد.
مشکل عدم وجود ویژگی "non-existing property"
اگر تازه شروع به آموزش و یادگیری جاوا اسکریپت کرده اید، شاید هنوز شما این مشکل را لمس نکرده باشد، اما بسیار رایج است.
به عنوان مثال، فرض کنید ما اشیاء کاربری (user objects) داریم که اطلاعات مربوط به کاربران ما را در خود نگه می دارد.
اکثر کاربران ما آدرس هایی در ویژگی user.address با خیابان user.address.street دارند، اما برخی آن ها را ارائه نکرده اند.
در چنین حالتی، وقتی سعی می کنیم user.address.street را دریافت کنیم و کاربر بدون آدرس باشد، با خطا مواجه می شویم:
این نتیجه مورد انتظار است. جاوا اسکریپت به این صورت عمل می کند. از آنجایی که user.address تعریف نشده است، تلاش برای دریافت user.address.street با یک خطا ناموفق است.
در بسیاری از موارد عملی، ما ترجیح می دهیم به جای یک خطا در اینجا، تعریف نشده (undefined )، دریافت کنیم (به معنی "بدون خیابان").
یک مثال دیگر را در نظر بگیرید. در توسعه وب، می توانیم با استفاده از فراخوانی یک متد خاص، مانند document.querySelector('.elem') شیئی را دریافت کنیم که با عنصر صفحه وب مطابقت دارد، و زمانی که چنین عنصری وجود نداشته باشد، تهی را برمی گرداند.
یک بار دیگر، اگر عنصر وجود نداشته باشد، با خطای دسترسی به ویژگی .innerHTML مربوط به null مواجه خواهیم شد. و در برخی موارد، زمانی که عدم وجود عنصر طبیعی است، ما ترجیح می دهیم که از خطا اجتناب کنیم و فقط html = null را به عنوان نتیجه می پذیریم.
چگونه می توانیم این کار را انجام دهیم؟راه حل واضح این است که مقدار را با استفاده از if یا عملگر شرطی ?، قبل از دسترسی به ویژگی آن، مانند زیر بررسی کنید:
این کار می کند، هیچ خطایی وجود ندارد اما کاملاً بی ظرافت است. همانطور که می بینید، "user.address" دو بار در کد ظاهر می شود.
در اینجا نحوه جستجوی همان برای document.querySelector نشان داده شده است:
;let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null
می بینیم که عنصر جستجو document.querySelector('.elem') در واقع دو بار در اینجا فراخوانی می شود که خوب نیست.
برای خواص تو در تو عمیق تر، زشت تر می شود، زیرا به تکرارهای بیشتری نیاز است.
به عنوان مثال. بیایید user.address.street.name را به روشی مشابه دریافت کنیم:
let user = {}; // user has no address
;alert(user.address ? user.address.street ? user.address.street.name : null : null)
چیزی که می بینید وحشتناک است، حتی ممکن است در درک چنین کدهایی مشکل داشته باشید.
روش کمی بهتری برای نوشتن آن، با استفاده از عملگر && وجود دارد:
AND کردن کل مسیر به ویژگی تضمین می کند که همه اجزا وجود دارند (اگر نه، ارزیابی متوقف می شود)، اما ایده آل نیست.
همانطور که می بینید، نام های ویژگی هنوز در کد تکراری است. به عنوان مثال. در کد بالا، user.address سه بار ظاهر می شود.
به همین دلیل است که optional chaining برای حل این مشکل به زبان برنامه نویسی Javascript اضافه شد. یک بار برای همیشه!
Optional chaining
عبارت .؟ optional chaining ارزیابی را متوقف می کند اگر مقدار قبل از .? تعریف نشده (undefined) یا تهی (null) باشد و تعریف نشده را بر می گرداند.
در ادامه این مقاله، برای اختصار، می گوییم که چیزی «وجود دارد» (exists) اگر تهی و تعریف نشده نباشد.
به عبارت دیگر، value?.prop :
1- اگر مقدار وجود داشته باشد، مانند value.prop کار می کند،
2- در غیر این صورت (زمانی که مقدار تعریف نشده/تهی باشد) تعریف نشده برمی گردد.
در ادامه راه امن و مطمئن برای دسترسی به user.address.street با استفاده از .? نشان داده شده است:
همانطور که می بینید کد کوتاه و تمیز است، هیچ تکراری وجود ندارد.
در اینجا یک مثال با document.querySelector آورده شده است:
خواندن آدرس با user?.address حتی اگر شیء کاربر وجود نداشته باشد کار می کند:
توجه داشته باشید که سینتکس .? مقدار قبل از آن را اختیاری می کند، اما نه بیشتر.
به عنوان مثال در user?.address.street.name، سینتکس .? به user اجازه می دهد تا با خیال راحت تهی/تعریف نشده باشد (و در این صورت تعریف نشده را بر می گرداند)، اما این فقط برای user است. property های بیشتر به صورت منظم قابل دسترسی هستند. اگر می خواهیم برخی از آنها اختیاری باشند، باید تعداد بیشتری را با .? جایگزین کنیم.
از optional chaining بیش از حد استفاده نکنید!
باید فقط در جایی که اشکالی ندارد چیزی وجود نداشته باشد از ?. استفاده کنیم.
به عنوان مثال، اگر طبق منطق کد ما شی user باید وجود داشته باشد، اما آدرس اختیاری است، باید user.address؟.street را بنویسیم، اما نه user?.address؟.street.
سپس، اگر user تعریف نشده باشد، یک خطای برنامه نویسی در مورد آن می بینیم و آن را برطرف می کنیم. در غیر این صورت، اگر بیش از حد از .? استفاده کنیم، خطاهای کدنویسی می توانند در جایی که مناسب نیستند خفه شوند و اشکال زدایی آنها دشوارتر شود.
متغیر قبل از .? باید اعلام شود!
اگر اصلاً هیچ متغیر user وجود نداشته باشد، user?.anything یک خطا را ایجاد می کند:
متغیر باید اعلان شود (به عنوان مثال let/const/var user یا به عنوان پارامتر تابع). optional chaining فقط برای متغیرهای تعریف شده کار می کند.
اتصال کوتاه (Short-circuiting)
همانطور که قبلا گفته شد، در .? اگر قسمت چپ وجود نداشته باشد، .? ارزیابی را بلافاصله متوقف می کند ("اتصال کوتاه").
بنابراین، اگر فراخوانی تابع یا عملیات دیگری در سمت راست .? وجود داشته باشد، آنها انجام نخواهند شد.
برای مثال:
انواع دیگر : ().? , [].?
زنجیر اختیاری .? یک عملگر نیست، بلکه یک ساختار نحوی خاص است که با توابع و براکت های مربعی نیز کار می کند.
به عنوان مثال،().? برای فراخوانی تابعی استفاده می شود که ممکن است وجود نداشته باشد.
در کد زیر، برخی از کاربران ما دارای متد admin هستند و برخی دیگر ندارند:
</br >در اینجا، در هر دو خط، ابتدا از نقطه (userAdmin.admin) برای دریافت ویژگی admin استفاده می کنیم، زیرا فرض می کنیم که شی user وجود دارد، بنابراین از روی آن قابل خواندن است.
سپس ().? قسمت سمت چپ را بررسی می کند: اگر تابع admin وجود داشته باشد، اجرا می شود. (این برای userAdmin نیز صادق است). در غیر این صورت (برای userGuest) ارزیابی بدون خطا متوقف می شود.
نحو [].? نیز کار می کند، اگر بخواهیم از براکت [] برای دسترسی به ویژگی ها به جای نقطه استفاده کنیم. مشابه موارد قبلی، اجازه می دهد به طور ایمن یک ویژگی را از یک شی که ممکن است وجود نداشته باشد، بخوانیم.
</br >همچنین می توانیم از .? با delete استفاده کنیم:
می توانیم از .? برای خواندن و حذف امن استفاده کنیم ، اما نه برای نوشتن.
.? optional chaining در سمت چپ یک تخصیص هیچ کاربرد و استفاده ای ندارد.
به عنوان مثال:
خلاصه مطالب
سینتکس .? optional chaining دارای سه فرم است:
1- obj?.prop - در صورت وجود obj، مقدار obj.prop را برمی گرداند، در غیر این صورت undefined را بر می گرداند.
2- obj?.[prop] - در صورت وجود obj، مقدار obj[prop] را برمی گرداند، در غیر این صورت undefined را بر می گرداند.
3- ().?obj.method - در صورت وجود obj.method ، مقدار ()obj.method را فراخوانی می کند، در غیر این صورت undefined را برمی گرداند.
همانطور که می بینیم، همه آنها ساده و آسان برای استفاده هستند. .? قسمت سمت چپ را از نظر تهی/تعریف نشده بررسی می کند و در صورت عدم وجود، اجازه می دهد ارزیابی ادامه یابد.
یک زنجیره از .? اجازه می دهد تا با خیال راحت به ویژگی های تو در تو دسترسی داشته باشید.
با این حال، ما باید با دقت از آن استفاده کنیم، فقط در جایی که طبق منطق کد ما قابل قبول است که قسمت چپ وجود نداشته باشد. به طوری که در صورت بروز خطاهای برنامه نویسی آنها را از ما پنهان نکند.