تایپاسکریپت - چگونه OOP را به FP در جاوااسکریپت تبدیل کنیم
من این کد را در جاوااسکریپت دارم
class StringFormatter {
init(locale) {
this.locale = locale;
}
getA() {
return 'A' + this.locale
}
getB() {
return 'B' + this.locale
}
getC() {
return 'C' + this.locale
}
}
// when page just render
let stringManager = new StringFormatter();
stringManager.init('enUS');
// call it in other components
let result = stringManager.getA();
پس زمانی که من از این کلاس OOP استفاده میکنم، فقط نیاز دارم که locale را یک بار با init کردن آن هنگام اولین بار رندر شدن صفحه تنظیم کنم. و سپس تمام کدهای بعدی که متدهای getA, getB یا getC را فرا میخوانم، با این locale سازگار خواهند شد.
حال که میخواهم این را به یک روش برنامهنویسی تابعی منتقل کنم، چگونه میتوانم آن را بنویسم اما همچنان همان مفهوم را حفظ کنم. در حال حاضر تنها میتوانم این راهحل را پیدا کنم
getA(locale) {
return 'A' + locale
}
getB(locale) {
return 'B' + locale
}
getC(locale) {
return 'C' + locale
}
let result = getA('enUS');
اما این یکی باعث شد که من هر بار که تابعهای getA, getB یا getC را صدا میکنم، locale را ارسال کنم.
آیا راهی وجود دارد که ما بتوانیم به هر نحوی همچنان محلی را فقط یک بار تنظیم کنیم، نیازی نباشد که هر بار آن را به توابع getA, getB یا getC پاس دهیم، و نیازی به نوشتن به روش OOP نداشته باشیم؟
پاسخ شماره ۱
از یک closure استفاده کنید:
let getA;
let getB;
let getC;
function initStringFormatter (x) {
let locale = x;
getA = function () { return 'A' + locale }
getB = function () { return 'B' + locale }
getC = function () { return 'C' + locale }
}
initStringFormatter('enUS');
getA();
از نظر تئوری، کلوزورها میتوانند دقیقا همان ویژگیهایی را که ویژگیها/متغیرها/اعضای اشیاء در برنامهنویسی شئگرا ارائه میدهند، فراهم کنند. یک رابطه یکبهیک دقیق بین کلوزورها و اشیاء وجود دارد. تنها تفاوت این است که کلوزورها از دامنه بهعنوان مکانیزم پیوست متغیرها به توابع استفاده میکنند در حالی که اشیاء از پیوندها برای پیوست متغیرها به متدها استفاده میکنند.
البته، هیچ اشکالی در ترکیب برنامهنویسی شیگرا با برنامهنویسی تابعی وجود ندارد. حتی زبان لیسپ هم یک کتابخانه برنامهنویسی شیگرا دارد. بنابراین یک اصطلاح رایج در جاوااسکریپت این است که به جای اینکه نام توابع را به عنوان متغیرهای جهانی داشته باشیم، یک شیء برگردانیم:
function initStringFormatter (locale) { // another trick is to just use
// the argument directly instead of
// creating another variable.
function a () { return 'A' + locale }
function b () { return 'B' + locale }
function c () { return 'C' + locale }
return {
getA: a,
getB: b,
getC: c
}
}
let stringManager = initStringFormatter('enUS');
stringManager.getA();
در واقع. معمول است که برنامهنویسان FP در جاوااسکریپت از اشیاء به این شکل استفاده کنند: صرفاً به عنوان فضاهای نام برای توابع. مدیریت وضعیت میتواند به صورت ۱۰۰٪ با استفاده از closures به جای ویژگیهای شیء انجام شود.
توضیح جانبی: برنامهنویسی تابعی به اندازهای قدرتمند است که زبانهایی مانند لیسپ نیازی به اینکه برنامهنویسی شیءگرا بهصورت داخلی در زبان باشد، ندارند؛ در عوض، برنامهنویسی شیءگرا یک الگوی طراحی و/یا یک کتابخانه است - کتابخانه استاندارد برنامهنویسی شیءگرا برای لیسپ، CLOS است: سیستم شیءگرای لیسپ عمومی
تنها نقطه منفی استفاده از کلوزرها این است که همه کلوزرها اساساً خصوصی هستند (فنیتر بگویم، آنها خصوصی نیستند، آنها فقط متغیرهای محلی در توابع هستند - خصوصی/عمومی مفاهیم باندینگ هستند و جهانی/محلی مفاهیم حوزه هستند). اما اگر تجربه قابل توجهی در برنامهنویسی شیءگرا داشته باشید، این را به عنوان یک رویه خوب در دنیای OOP میشناسید که هیچگاه دسترسی عمومی به متغیرها داده نشود. هنگام استفاده از کلوزرها، 100٪ دسترسی به متغیر باید از طریق توابع انجام شود - باز هم، در دنیای OOP این به الگوی طراحی getter/setter متداول مشهور است که به عنوان رویه خوب در نظر گرفته میشود.
با این حال توجه داشته باشید که این ۱۰۰٪ FP خالص نیست. اما اشکالی ندارد. زبانهای FP دیگری هم وجود دارند که خالص نیستند. جاوااسکریپت هنوز اجازه میدهد متغیرهای محصور شده تغییر کنند. با این حال، تابع بالا FP خالص است چون متغیر locale نمیتواند تغییر کند.
خالص کردن جاوااسکریپت به 100٪ تابعی (FP) ساده است. به سادگی تمام استفادهها از let و var را در کد خود ممنوع کنید و همیشه از const استفاده کنید. در ابتدا احساسی عجیب دارد، اما شما میتوانید هر برنامهای را فقط با استفاده از const و آرگومانهای تابع (که به طور پیشفرض ثابت هستند) بنویسید. زبانهایی مانند جاوااسکریپت و Go به شما این امکان را میدهند که از متغیرها برای مدیریت منطق مبتنی بر وضعیت بهطور مستقیم استفاده کنید.
پاسخ شماره ۲
شاید بخواهی یک فکر داشته باشی؟
یک تانک محاسبه را تا زمانی که نتیجه آن مورد نیاز باشد به تعویق میاندازد و ارزیابی تنبل آرگومانها را فراهم میکند. (تعریف تانک در رمدا)
const thunkGetFromLocale = (locale) => () => (toGet) => toGet + locale
let thunk = thunkGetFromLocale('en-US')()
console.log(thunk('A'))
console.log(thunk('B'))
console.log(thunk('C'))
thunk = thunkGetFromLocale('vi-VN')()
console.log(thunk('A'))
console.log(thunk('B'))
console.log(thunk('C'))