آشنایی با Proxy و ساخت یه سیستم Cache دم دستی باهاش

اگه قبلا اسم Proxy آبجکت ها باعث میشد که یهو بگید "ای بااباا این دیگه چیه... ولمون کن" باید بگم تنها نبودید ? منم تا قبل اینکه از قابلیت های فراوون Proxy ها خبر داشته باشم همچین فکری می‌کردم. ولی بعد که باهاش بیشتر آشنا شدم و فهمیدم چه کارای باحالی میشه باهاش کرد تصمیم گرفتم روش بیشتر فکر کنم و نظرم عوض شد!واقعا خفن و خوشگله. جلوتر باهاش بیشتر آشنا میشیم و یاد می‌گیریم چه کارایی میشه کرد باهاش و یه سیستم کشینگ خیلی جمع و جور هم باهاش می‌نویسیم.اگر قبلا با proxy ها آشنا هستید میتونید مستقیم برید سراغ قسمت "پیاده سازی سیستم کش با proxy"پیش زمینهاول یکم در مورد Object ها توی جاواسکریپت بگم. قطعا میدونید. چندتا عملیات خیلی ساده وجود داره. وقتی میخواید یک property رو از داخل object بخونید، وقتی میخواید یه property جدید داخل object ست کنید و وقتی میخواید ببینید یک property توی object وجود داره یا نه و در آخر حذف کردن یک property از داخل object.خلاصه میشه:خوندن پراپرتی یا getاضافه کردن یه پراپرتی جدید یا setبررسی وجود یک پراپرتی یا hasحذف یک پراپرتی یا deleteبه اینصورت هم نوشته میشن:const user = { name: 'hesan' }; // get user.name; // hesan // set user.height = 190; // { name: 'hesan', height: 190 } // has 'age' in user; // false 'name' in user; // true // delete delete user.name; // { height: 190 }تا اینجا هر چیزی گفتم تکراری بود، همه اینا رو میدونیم. حالا ببینیم proxy ها میتونن برامون چیکار کنن.پراکسیدر واقع Proxy ها یک نوع Object هستند که میان و دور آبجکت هدف شما (این آبجکت میتونه هر چیزی باشه) قرار می گیرن و یه لایه جدید بهش اضافه میکنن. کنترل کردن این proxy کاملا دست خودتونه. میتونید رفتار های خاص بهش بدید، کاری کنید که یه طور دیگه رفتار کنه و خیلی کارای خفنی میشه با این کرد.اون عملیات هایی که قبلا گفتم یه تعداد عملیات مشخصه، خارج از اینا نیست (البته چندتا دیگه هم هست که من بهش اشاره نکردم).پراکسی ها ابزار های بسیار قدرتمندی هستند که به کمکشون خیلی کار ها میشه انجام داد و حتی بعضی جا ها میشه بعنوان یک pattern ازش توی معماری اپلیکیشن استفاده کرد. لینک ۲ تا مطلب رو براتون قرار میدم که پیشنهاد میکنم حتما بخونیدشون که عمق توانایی های proxy رو بتونید درک کنید. این مطلب فقط قراره با یک سری از کار های سطحی و اولیه که پراکسی ها برامون میتونن انجام بدن آشنا بشیم. https://medium.com/front-end-weekly/proxy-observable-as-property-pattern-in-react-51d081569534 https://medium.com/front-end-weekly/todomvc-app-with-react-and-mlyn-6a2bffb08c32 ادبیات Proxy هاهمونطور که قبلا هم گفتم پراکسی ها دور یک object قرار میگیرن و کاری میکنن رفتار اون object کاستوم بشه و کنترلش بیوفته دست شما. ادبیاتی که برای پراکسی ها استفاده می‌کنیم چند تا کلمه مهم داره که باهاش آشنا میشیم.اولین کلمه کلیدی هست: Target. این target همون object هست که میخواید پراکسی دورش قرار بگیره. آبجکت هدفتونه. مثلا:const user = { name: 'hesan' }; // target object const proxyObject = new Proxy(user); // the proxy objectبرای ایجاد یک proxy هم از new Proxy استفاده میشه، ولی اگر الان شما این رو اجرا کنید یه exception میده با این مضمون که "به من handler ندادی"پس می‌رسیم به دومین کلمه مهم: Handler. میتونیم رفتار های خاص تعریف کنیم برای آبجکت جدیدمون که اینکار به کمک این handler انجام میشه. handler یه آبجکته که میتونیم توش بگیم وقتی داریم عملیات های مختلف رو روی اون target انجام میدیم میخواید چطور این اتفاق بیوفته و به این شکل هم تعریف میشه:const user = { name: 'hesan' }; // target object const handler = { // get trap get: (o, property) { return o[property]; } }; const proxyObject = new Proxy(user, handler); // the proxy objectهمینطور که میبینید، handler یه آبجکت ساده هست و توش میتونید چندتا متد تعریف کنید که به هر کدوم از اون متد ها میگیم trap. بله. پس Trap آخرین کلمه ای هست که یاد میگیریم. trap ها انواع مختلفی دارن برای کار های متفاوتی. رایج ترینشون get و set هست. لیست کامل این trap ها رو میتونید اینجا ببینید. دقت کنید که این کلمات تعریف شده هستند و طوری نیست که بتونید خودتون یه متد دلخواه تعریف کنید. من اینجا با ۳ تاشون کار دارم: get و set و hasخب میرسیم به توضیح اینکه این trap ها چی هستند. برای فهم بهترش این مثال رو ببینید:user.nameوقتی شما این رو می‌نویسید در واقع دارید یه property از آبجکت user رو میخونید، اگر این user یک proxy باشه، متد get داخل handler رو داره صدا میزنه و به کمک اون اطلاعات property رو بهتون میده.اینجا که کنترل دست شماست! میخواید مثل مثال بالا فقط اون property رو برگردونید یا میخواید کار دیگه هم انجام بدید؟همین داستان برای set و has به این شکل اتفاق میوفته:user.name = 'hesan'; // set 'name' in user; // hasبا نوشتن هر کدوم از موارد بالا متد هایی که جلوش کامنت کردم صدا زده میشن. این کد رو از اینجا کپی کنید و اجراش کنید: https://gist.github.com/hesan-aminiloo/6ceb705462783a7d52c2b1f2dddecfc0 خب اگه این کد رو اجرا کنید، یه پراکسی تخس درست کردیم که هر پراپرتی از توش بخوایم بخونیم میگه undefined هست و هر مقداری هم توش بخوایم ست کنیم بر عکسش رو ست میکنه و اگرم بخوایم ببینیم یک پراپرتی توش وجود داره میگه false.هر کدوم تابع های trap رو میتونید به میل خودتون بنویسید، من اینجا برای اینکه نشون بدم میشه هرکاری کرد کدش رو این شکلی نوشتم ولی خیلی کار های دیگه میشه کرد.امیدوارم تا اینجا با خود proxy آشنا شده باشید و قابلیت هایی که بهمون میده رو هم فهمیده باشید، میریم سراغ کاربردش تو دنیای واقعی.پیاده سازی سیستم کش به کمک Proxyقبل اینکه بریم سراغ مثال cache باید بگم که از proxy ها خیلی جاها استفاده میشه. از نمونه هاش میتونم بگم برای سیستم کشینگ، validation و حتی مثلا هندل کردن event ها (که مثلا React ازش استفاده میکنه).مثالی که من اینجا مینویسم خیلی ساده و الهام گرفته شده از مطالبی هست که قبلا خوندم، پس میدونم که ضعف هایی داره.این سیستم از این قراره که مثلا اگر شما دنبال دیتای یک user خاص هستید، اون رو از داخل object فراخوانی می‌کنید. ولی این آبجکت ما یک Proxy هست که اگر دیتا اون یوزر رو داشته باشه از سیستم کش بهتون میده و اگر نداشته باشه یه درخواست ارسال میکنه به سرور تا دیتاش رو بگیره و از اون به بعد اون یوزر با اون id داخل آبجکت cache میشه دیگه تماسی با سرور گرفته نمیشه. منطق این درخواست به سرور رو هم داخل get از handler های این پراکسی می‌نویسیم.برای اینکه مطلب طولانی نشه، من کدش رو قرار میدم و خط به خط توضیح میدم که چه اتفاقی داره میوفته. https://gist.github.com/hesan-aminiloo/3071e2cfe34f4075aec275fbd22efa87 خب این کل کار هست. خط به خط حالا توضیح میدم که چه خبره.از IIFE استفاده کردم برای اینکه بتونیم از await استفاده کنیم.آبجکت target تعریف شده که اول کار خالیه.یه function ساده برای گرفتن اطلاعات user که id میگیره و دیتا اون user رو برمی‌گردونه.آبجکت handler اینجا تعریف شده. به متد get نگاه کنید، اینجا جاییه که اول بررسی میکنه که اگر target دارای id درخواست شده بود، مستقیماً همون رو برمی‌گردونه (قبلا کش شده) و در غیر اینصورت user رو به کمک تابعی که قبلا نوشتیم دریافت میکنه. بعد از دریافت یوزر اون رو داخل object ذخیره میکنه که به کمک یک trap دیگه که درواقع همون set هست یه کلید به اسم cacheTime بهش اضافه میکنه که صرفاً متوجه بشیم که این مقدار قبلا cache شده.آبجکت Proxy ایجاد شده به اسم usersProxyیه متغیر به اسم user1 تعریف شده که یوزر با آیدی 1 رو میخواد. اینجا چون قبلا یوزری با همچین id قبلا داخل proxy وجود نداشته، یه درخواست برای سرور ارسال میشه و اطلاعات اون یوزر رو میگیره و برمیگردونه و بعد هم ازش console.log گرفتم.یه متغیر دیگه به اسم user2 تعریف کردم ولی مجدد همون id 1 رو میخوام. این دفعه بعد از اینکه user2 رو لاگ گرفتم، داخل آبجکت Proxy وجود داره و از cache مقدار رو برمی‌گردونه، اینبار یک پراپرتی اضافه تر هم داره با اسم cacheTime که اشاره میکنه به لحظه ای که این دیتا cache شده.یه مثال Validation هم باهاش ببینیم ضرر نداره https://gist.github.com/hesan-aminiloo/f2f11996c0ca2e6586dd16b100398d8b خب ببینیم تو این مثال چه خبره:داخل handlers فقط از یه دونه set trap استفاده کردم، target که اشاره میکنه به همون آبجکت target و property همون مقداری هست که میخوایم ست کنیم. اینجا قبل از اینکه چیزی رو ست کنم بررسی کردم که اگر email میخواست ست بشه قبلش ببین آیا با این pattern که بهش گفتم match میشه یا نه. اگر match شد که داخل target قرار می‌گیره و مشکلی نیست. ولی اگر نشد یه exception میده و میگه که فرمت ایمیلی که دادی درست نیست.این یه خلاصه خیلی کوچیکی بود که من با سواد نداشته ام، سعی کردم یکم در مورد قابلیت هایی که Proxy ها بهمون میدن و خودم بلدم صحبت کنم. امیدوارم شما بتونید کار های خیلی خفن تری باهاش انجام بدید.دمتون گرم که تا اینجای مطلب رو خوندید، اگر دوست داشتید میتونید از طریق لینکدین باهام در ارتباط باشید!موفق باشید!