Node Master
1.02K subscribers
24 photos
2 files
156 links
Group Chat: @nodemastergp
Admin: @napoleon_n1
Download Telegram
Node Master
خب هنوز یک قدم میخوایم پا رو فراتر بزاریم و class رو در runtime بسازیم. اول این که در #Javascript کلاس ها در حقیقت همون فانکشن ها هستن و صرفا کلمه class یک syntactic sugar روی نسخه ES5 هست. کلمه class در آپدیت معروف ES6 به #Javascript اضافه شد. یعنی این دوتا…
در پست قبل یاد گرفتیم که چطور یک کلاس در Runtime بسازیم. و موضوعی باز موند که چطور میتونیم اسم کلاس ها هم در Runtime تغییر بدیم. برای این کار نیاز به یک تکنیک دیگه که در #Metaprogramming خیلی پر استفاده هست باید استفاده کنیم. به اسم Reflection. این تکنیک در اکثر زبان ها به طریقی وجود داره. golang و java دقیقا lib های خیلی قوی مربوط به این تکنیک دارند. متاسفانه Reflection در #Javascript خیلی محدود هست و قدرت زبان های دیگ رو نداره ولی اینجا کاربرد داره.
- حالا Reflection چیست؟ در حقیقت Reflection به معنی توضیح دادن یک کد با استفاده از یک کد دیگه.
زبان Lisp یک زبان قدیمی هست و استفاده خاصی این روزا نداره ولی یک فلسفه خیلی معروف به دنیای برنامه نویسی معرفی کرد.
- "Code Is Data"
بخوایم توضیح بالا رو کامل تر و ساده تر کنیم یعنی با کد برنامه مثل دیتا معمولی رفتار کنیم! این هم بگم اگر یکم درکش سخت هست براتون کاملا منطقیه. این نوع تفکر نیاز به تمرین زیادی داره پس زیاد سخت نگیرید.
راجع به Lisp و این موضوع در آینده بیشتر صحبت میکنیم.

حالا کد بالا رو در نظر بگیرید. و به مثال زیر دقت کنید.
function buttonFactory(color) {
const cls = class {
#color = color;
get color() {
return this.#color;
}

show() {
console.log("Button is " + this.#color);
}
};
Reflect.defineProperty(cls, "name", {
writable: false,
value: color[0].toUpperCase() + color.slice(1) + "Btn",
});
return cls;
}

برای این که کد بالا رو توضیح بدم اول این نکته رو باید در نظر داشته باشید که هر function یک attr به اسم name داره که اسم فانکشن هست. اگر راهی پیدا کنیم که بتونیم این رو در runtime تغییر بدیم پس مشکلی نخواهیم داشت.
function SayMyName() {}
console.log(SayMyName.name);

در #Javascript دو namespace برای کمک به ما در Reflection وجود دارد.
- Reflect
- Object
در این name space ها یک سری static method برای کمک هستن که در کد بالا نمونه ای میبینید که ما اومدیم یک cls رو ساختیم و reference اون رو نگهداری میکنیم. بعد با استفاده از اون ref و کمک reflection در runtime به راحتی name رو به چیزی که میخوایم تغییر میدیم. بعد ref رو return میکنیم.

نکته در مورد مثال:
- خیلی خلاصه گویی شده به دلیلی که مطلب طولانی نشه.
- اینجا هدف فقط اشاره و لمس این موضوع هست در آینده بیشتر صحبت خواهیم کرد.
👍14
اگر تا حالا فکر کردین که چطور فریمورک ها رو برای ما developer های عادی توسعه میدن جواب شما یک magic هست به اسم #Metaprogramming . این کار مثل برنامه نویسی معمولی که ما انجام میدیم تکنیک های مختلف داره که یکی از اونها رو در این پست باهم برسی کردیم.
https://t.iss.one/NodeMaster/115

البته لازم به ذکر هست یکسری Pattern به صورت کلی وجود داره که بین زبان های مختلف مشترک هست و به نوعی پیاده سازی های مختلف ازش دیده میشه و البته یکسری زبان ها هم یکسری تکنیک های مخصوص به خودشون رو هم دارن که اون ها رو خاص تر میکنه.

حالا یکی از این جادو های کاربردی #Metaprogramming این هست که اگر یک Instance از یک Object داریم که یک Parrent Class داره چطور فقط و فقط متوجه بشیم که چه Attr هایی مخصوص به اون Child Class هست و Attr های Parrent رو نادیده بگیریم. و جواب این سوال خیلی سادس :
const myPrototype = { x: 1, y: 2 }; // Parrent
const myObj /* Child */ = Object.create(myPrototype);

myObj.name = "point"; // Instance Prop

console.log(Object.getOwnPropertyNames(myObj)); // [ 'name' ]
console.log(Reflect.ownKeys(myObj)); // [ 'name' ]
console.log("x in myObj :", "x" in myObj);

در #JavaScript با استفاده از Object.getOwnPropertyNames و Reflect.ownKeys(myObj) میتونیم متوجه بشیم که کدام Atter ها مربوط به Instnace ما یعنی myObj هستند. به این خاطر هست که Atter های Parrent یعنی x و y رو کامل نادیده گرفته میشه و فقط در name رو ما به عنوان Attr میبینم که مستقیما روی Child Instance ما تعریف شده و نه روی Parrent. شاید این مثال یکم پیچیده باشه بخاطر ماهیت Object ها در #JavaScript هست. همین رو اگر بخوایم در #Python برسی کنیم شاید یکم قابل درک تر باشه و البته مثال های دیگ هم در #JavaScript میشه زد ولی خب به نظرم بهتره بزاریم برای کنجکاوی خودتون.
class Parrent:
def __init__(self) -> None:
self.x = 1
self.y = 2

class Child(Parrent):
def __init__(self) -> None:
super().__init__()
self.name = "point"


myObj = Child()

parrentAttrs = set(dir(Parrent()))
childAtters = set(dir(Child()))

print(childAtters.difference(parrentAttrs))

یک نکته خیلی جذاب کلا خارج از بحث که به نظرم ارزش توجه کردن داره استفاده از set برای رسیدن به نتیجه ای شبیه به مثال #JavaScript هست. و اینجا به عنوان مثال مستقیم یکی از کاربرد های Set Object و Set theory رو مستقیم و خیلی کوچیک میبینید.
نکته ای که خیلی مهم باید توجه بشه این هست که بین تکنیک استفاده از ownKeys و getOwnPropertyNames تفاوت هایی وجود داره که اگر استقبال بشه عمیق تر وارد این موضوع میشیم.

#Tip
👍8