34 - جاوااسکریپت: Functions are Objects

تعریف first class function: هر چیزی که میتوانیم با انواع دیگر انجام دهیم (شیء ها، string ها، عددها، boolean ها، ... ) با تابع ها هم میتوانیم انجام دهیم. میتوانیم یک تابع را به عنوان یک مقدار به متغیری assign کنیم. میتوانیم تابع ها را به عنوان پارامتر به تابع دیگری پاس دهیم. میتوانیم on the fly تابع ها را با literal syntax ایجاد کنیم. first class function سبک برنامه نویسی را تغییر میدهد. میتواند افق های کاملا متفاوتی را در حل مسئله ایجاد کند.وقتی میگوییم که در js تابع ها شیء هستند، یک شیء تابع چه شکلی است؟ شبیه هر شیء دیگری در js است. یک نوع خاص از شیء است. چون همه ویژگی های یک شیء نرمال را دارد و ویژگی های خاص دیگری هم دارد. یکی از چیزهایی که افراد را در مورد js سورپرایز میکند این است که میتوانیم در js به یک تابع property ها و method هایی را attach کنیم. چون تابع یک شیء است. پس میتوانیم به یک تابع یک Primitive (یعنی name/value) را attach کنیم. میتوانیم یک Object را attach کنیم. میتوانیم یک Function دیگر را attach کنیم. در js یک تابع ویژگی های دیگری هم دارد.مهم ترین ویژگی آن NAME آن است. یک تابع در js الزامی به داشتن اسم ندارد. اگر اسم نداشته باشد anonymous نامیده میشود. ولی میتواند اسم داشته باشد.یک ویژگی دیگر آن CODE است. جایی که کدها قرار گرفته اند. کدی که نوشته ایم به صورت یک ویژگی خاص از شیء تابع در می آید. پس کدی که نوشته ایم تابع نیست. تابع یک شیء است با یک سری ویژگی و کدی که نوشته ایم یکی از ویژگی های آن است. یک نکته در مورد این ویژگی این است که invocable است. یعنی میتوانیم بگوییم که کد داخل این ویژگی را اجرا کن و execution context ایجاد میشود و ....باید به تابع به چشم یک شیء نگاه کنیم که کد آن فقط یک ویژگی آن است. تابع میتواند جابجا شود و کپی شود و به المنتها یا بخش هایی از کدمان داده شود، مثل همه شیء های دیگر. مثل یک string یا یک عدد. برای اینکه از روش های حل مسئله استفاده یا سینتکس ها را متوجه شویم باید به تابع به این شکل نگاه کنیم.function greet() { console.log('Hi'); }چون تابع یک شیء است، پس با استفاده از . میتوانیم برای آن property ایجاد کنیم:greet.language = 'english';در زبانهای برنامه نویسی دیگه چنین چیزی امکان پذیر نیست. اگر در console بخواهیم greet را ببینیم به ما text تابع را که نوشته ایم نشان میدهد:function greet() { console.log('Hi'); }و اگر در console بخواهیم greet.language را ببینم به ما english را برمیگرداند.پس وقتی چنین تابعی را ایجاد میکنیم، یک شیء تابع در حافظه قرار میگیرد. در این مثال در global object قرار میگیرد. یک ویژگی NAME با مقدار greet دارد و یک ویژگی CODE دارد که کد داخل تابع را شامل میشود. وقتی تابع را با greet(); فراخوانی میکنیم ویژگی CODE م invoce میشود و execution context ایجاد میشود و ... . پس تابع فقط یک نگهدارنده کد نیست، یک شیء است.35 - جاوااسکریپت: Function Statements, Function Expressionsدیدیم که در js تابع شیء است. میخواهیم بدانیم چگونه js به ما اجازه میدهد کارهای جالب و قدرتمندی با مفهوم first class function انجام دهیم. ابتدا نیاز هست فرق بین تعریف و استفاده از function statement و function expression را بدانیم.تعریف expression: بخشی از کد که مقداری را برمیگرداند. الزامی به ذخیره متغیر ندارد. statement فقط کاری را انجام میدهد ولی expression مقداری را برمیگرداند.مثلا وقتی در کد فقط داریم var a; اگر به console بریم a را در حافظه داریم. اگر در همان کنسول expression م a=3; را بنویسیم، میدانیم که = یک عملگر است. یعنی یک تابع که دو مقدار a و 3 را میگیرد و یک مقدار را برمیگرداند. در این مثال 3 را برمیگرداند. اگر در همین کنسول expression دیگری مثل 1+2; را بنویسیم، + یک عملگر است که مقادیر 1 و 2 را میگیرد و آنها را با هم جمع کرده و نتیجه را برمیگرداند. با اینکه نتیجه را داخل هیچ متغیری در حافظه ذخیره نکرده ایم ولی مقدار 3 به ما برگردانده شده. این مقدار برگرداند شده میتواند string یا شیء یا ... باشد. مثلا اگر در کنسول بنویسیم a={greet:'Hi'}; مقدار Object{greet:'Hi'} برگردانده میشود.اما وقتی داریم در مورد statement صحبت میکنیم، مثل if statement، داخل پرانتز جلوی if یک expression قرار میدهیم چون مقداری را به عنوان نتیجه برمیگرداند ولی خود if statement مقداری را برنمیگرداند. مثلا نمیتوانیم بگوییم var b = if (...) { ... }. ولی داخل پرانتز آن باید expression داشته باشیم.در js چون تابع ها شیء هستند، هم function statement و هم function expression داریم. مثلا میخواهیم یک function statement تعریف کنیم:function greet() { console.log('Hi'); }وقتی این تابع اجرا میشود مقداری را نتیجه نمیدهد. تابع در حافظه قرار دارد اما فقط یک statement است و تا زمانی که اجرا نشود مقداری برنمیگرداند. پس وقتی کد تابع را میبینیم یعنی تابع را در حافظه قرار دادیم ولی این تکه کد مقداری را نتیجه نمیدهد ولی کارهای خاصی را انجام میدهد، hoisted میشود و در مرحله execution در execution context در حافظه قرار میگیرد و میتواند در دسترس قرار بگیرد. میتوانیم greet را قبل از تعریف آن در کد فراخوانی کنیم. یعنی در حافظه در دسترس است.حالا میخواهیم از یک function expression استفاده کنیم:var anonymousGreet = function() { console.log('Hi'); };چون در js تابع یک شیء است میتوانیم کد را به صورت بالا بنویسیم. پس در کد بالا داریم یک شیء رو on the fly ایجاد میکنیم و آن را به متغیر anonymousGreet مان assign میکنیم. این متغیر قسمتی از حافظه است که به آدرسش اشاره میکند. با این کد این متغیر حاوی یک شیء تابع است.در function statement بالا وقتی execution context ایجاد میشد تابع در حافظه قرار میگرفت و ویژگی NAME و CODE داشت. وقتی آن را فراخوانی میکردیم به آن قسمت از حافظه که تابع بود متصل میشد و قسمت CODE را فراخوانی میکرد. اما در function expression یک عملگر = داریم که این تابع را در حافظه قرار میدهد و اشاره میکند به آدرس متغیر anonymousGreet. به اسم تابع نگاه نمیکند، فقط آدرس را در حافظه میداند. پس در این حالت هم موتور js یک شیء تابع ایجاد میکند ولی در این حالت NAME نداریم (anonymous) چون قبل از پرانتز چیزی قرار نداده ایم و لزومی هم به این کار نیست، چون متغیری داریم که میداند تابع کجا هست. پس به اسمی نیاز ندارم که با آن ارجاع دهیم. پس anonymous function تابعی است که در ویژگی NAME ش اسمی ندارد و با اسم یک متغیر به آن ارجاع میدهیم. ولی همان ویژگی CODE را دارد. برای فراخوان این تابع نیاز هست به شیء اشاره کنیم و به آن بگوییم کد را اجرا کند: anonymousGreet(); متغیر به جایی که تابع هست اشاره میکند و با گذاشتن پرانتز کد را invoce میکنیم.در کدvar anonymousGreet = function() { console.log('Hi'); };قسمت function() { console.log('Hi'); } یک function expression است چون یک مقدار نتیجه میدهد. این قسمت قابل اجراست. اما در کد function greet() { console.log('Hi'); }فقط function statement داریم. چون وقتی کد اجرا میشود حتی با اینکه تابع در حافظه وجود دارد، این قسمت از کد در مرحله execution اجرا میشود. واقعا کاری نمیکند، فقط میگوید که یک تابع آنجا هست. ولی وقتی خطvar anonymousGreet = function() { console.log('Hi'); };اجرا میشود، قسمتfunction() { console.log('Hi'); }در آن باعث ایجاد یک شیء میشود و یک تابع را برمیگرداند. پس یک expression است.اشتباه نیست اگر برای تابع زیر اسم بگذاریم:var anonymousGreet = function greet() { console.log('Hi'); };ولی کار اضافی است. کد خوب باید شفاف و قابل فهم و در عین خلاصه باشد.حالا اگر خط های کد را جابجا کنیم:anonymousGreet ();var anonymousGreet = function() { console.log('Hi'); };میدانیم که execution context ایجاد میشود و سپس اجرا میشود. ابتدا function statement ها و متغیرها را در حافظه قرار میدهد و سپس کد را خط به خط اجرا میکند. پس خروجی کد بالا: undefined is not a function. در اینجا موتور js یک متغیر میبیند و متغیر را در حافظه قرار میدهد. قبل از شروع اجرای کد مقدار متغیرها را undefined میگذارد و سپس شروع به اجرای کد میکند. وقتی به خط anonymousGreet(); میرسد میگوید که آن را در حافظه دیده و undefined بوده. در این خط با پرانتز خواسته ایم که کد تابع را اجرا کند، پس js به ما میگوید که undefined تابع نیست. پس function expression ها hoisted نیستند. چون فقط متغیر را hoisted میکند و متغیر تا قبل از اجرا شدن آن خط از کد حاوی شیء تابع نیست. پس حتما باید اول از عملگر = استفاده کنیم تا در حافظه قرار بگیرد و سپس آن را فراخوانی کنیم.ولی وقتی در کد داریم:greet();function greet() { console.log('Hi'); }تابع hoisted است. یعنی وقتی موتور js کلمه function را میبیند میفهمد که تابعی ایجاد کردیم و آن را در حافظه قرار میدهد. پس greet(); را اجرا میکند و میبیند که در حافظه وجود دارد. سپس به function statement میرسد و کاری نمیکند.یک تابع را با function statement ایجاد کرده ایم:function log(a) { console.log(a); }مثلا log(3) را اجرا میگیریم. متغیر 3 را on the fly ایجاد کرده ایم. مثلا قبلش نگفتیم var b = 3; و بعد log(b);. خروجی 3 میشود. اگر log({greeting:'Hi'}); را اجرا بگیریم در خروجی Object{greeting:'Hi} داریم. در اینجا هم پارامتر شیء را on the fly ایجاد کردیم و به تابع log پاس دادیم. همان طور که گفتیم تابع یک شیء است و یک function expression یک شیء on the fly ایجاد میکند. پس میتوانیم این کار را انجام دهیم: log( function(){ console.log('Hi'); } );تابع را on the fly ایجاد کردیم. خروجی به ما متن تابعی که نوشتیم را میدهد: function(){ console.log('Hi'); }. این یک تابع anonymous است اما با پارامتر a به آن ارجاع میدهیم.پس first class function باعث میشود که تابع پاس داده شود یا on the fly ساخته شود یا متغیری برابر با تابع قرار داده شود. در مثال بالا یک تابع را به تابعی پاس دادیم و a دارد به تابع اشاره میکند. پس برای اجرای تابعی که به log دادیم از a(); استفاده میکنیم:function log(a) { a(); }log( function(){ console.log('Hi'); } );وقتی در خط دوم log را فراخوانی میکنیم و با function expression یک تابع به آن پاس میدهیم، یک تابع همانجا ایجاد کرده ایم. پس a یک تابع شده و به جایی که این شیء هست اشاره میکند. سپس با a(); کد اجرا میشود.پس یک function statement داریم که در حافظه در مرحله creation execution context م hoisted میشود. یک function expression داریم که قسمت از عملگر = است و در متغیری قرار میگیرد و برای فراخوانی تابع از آن متغیر استفاده میکنیم. و یک function expression داریم که به یک تابع دیگه به عنوان پارامتر پاس میدهیم. این ویژگی first class function است که میتوانیم تابع را به تابع دیگری پاس دهیم یا از آن مثل یک متغیر استفاده کنیم و از سبک جدید برنامه نویسی به نام functional programming استفاده کنیم.پس چنین خط کدی:log( function(){ console.log('Hi'); } );همان function expression است و به خاطر first class function چنین امکانی وجود دارد. چون تابع ها در js شیء هستند.