زیر پوست جاوااسکریپت | 1.1: بالاخره کامپایلری یا مفسری؟

بسم الله الرحمن الرحیم

<پیش_نوشت>

یک سلام پر انرژی به شما که در حال مطالعه این مطلب هستی.

بازم همون عکس قبلی!
بازم همون عکس قبلی!

بالاخره بعد از 3 ماه فرصت شد تا قسمت اول این دنباله رو بنویسم. برای شروع کار با هر تکنولوژی جدیدی بهتره ابتدا کمی از ساختارش مطلع بشیم. این که بدونیم در پشت زمینه داره چه اتفاقی می‌افته حتی اگر اندک باشه باعث میشه نتیجه خیلی بهتری بگیریم.

برای شروع از شما می‌خوام جاوااسکریپت رو توصیف کنید به احتمال خیلی زیاد در بین توصیفات‌تون از این چند تا کلید واژه استفاده می‌کنید:

  • سطح بالا
  • مفسری (و نه کامپایلری)
  • داینامیک تایپ
  • دارای وابستگی کم به نوع (می دونم عجیبه ولی ترجمه weakly-typed هست!)

(البته احتمالا از واژه های اعصاب خرد کن، شلخته و... هم استفاده می‌کنید!!)

در واقع چهار تا موضوع بالا، چهار محور کلی برای تقسیم بندی زبان های برنامه نویسی هستن. موضوع اصلی اینجاست که چهار خط بالا رو احتمالا 90 درصد از خوانندگان این مطلب از قبل می دونستن. اما مطمئنم اکثر ما واقعا نمی‌دونیم چرا جاوااسکریپت مفسری هست و اصلا مفسری بودن واقعا یعنی چی؟ داینامیک تایپ بودن واقعا یعنی چی و سوالاتی از این دست.

خب دیگه مقدمه چینی کافیه! بیاید شروع کنیم و جواب "واقعی" این سوالات رو پیدا کنیم! در این مطلب به بحث مفسری/کامپایلری بودن جاوااسکریپت می‌پردازیم و در مقاله های بعد بقیه موارد رو بررسی می‌کنیم!

</پیش_نوشت>




<نوشتار>

یکم: جاوااسکریپت زبانی مفسری است! (بعید میدونم!)

با بحث نسبتا شیرین کامپایلری بودن یا مفسری بودن شروع میکنیم. گفتم که قصد ندارم همون چیزهایی که همه میدونیم و هزار بار شنیدیم رو دوباره بگم و دنبال یک جواب واقعی هستم. پس خیلی سریع و بدون توضیحات اضافه کامپایلر و مفسر رو معرفی میکنم تا به بحث اصلی خودمون برسیم. اگر دوست دارید بیشتر در رابطه با این دو تا بزرگوار بدونید، مطمئن باشید گوگل خوشحال میشه که بهتون کمک کنه :) جدا از شوخی، مطالعه این لینک برای درک این مسئله بسیار کاربردی هست :

A Deeper Inspection Into Compilation And Interpretation


ماشین زبان ما را نمی‌فهمد!

برای اینکه علت وجود کامپایلر و مفسر رو بدونیم، همین جمله ساده کافیه. ماشین (همون کامپیوتر) فقط زبان مختص به خودش رو می‌فهمه(فرض کنید زبان خودش صفر و یکه). ما میتونیم به صفر و یک برنامه بنویسیم؟ احتمالا نه! پس یه مترجم اینجا لازمه که زبان ما رو به زبان قابل فهم برای ماشین تبدیل کنه. کامپایلر(compiler) و مفسر(interpreter) دو تا از مترجم هایی هستن که زحمت تبدیل کد ما به کد ماشین رو می‌کشن (دست شون درد نکنه)

کامپایلر (compiler)

کامپایلر یک مترجم با صبر و حوصله است. ایشون ابتدا تمام کد منبع (source code) رو به کد ماشین تبدیل می‌کنه، سپس (معمولا) یه فایل خروجی قابل اجرا در اختیار کاربر قرار میده که کاملا ترجمه شده هست و از این به بعد کاربر فقط لازمه اون فایل رو اجرا کنه. مثل مترجمی که یک مقاله رو کاملا ترجمه میکنه و در اختیار شما میزاره.

مفسر (interpreter)

اگر در دیکشنری به دنبال معنای interpreter بگردید متوجه میشید که معمولا به "مترجم شفاهی" ترجمه میشه.

دقیقا مثل معنای لغوی اش، مفسر یک خط از برنامه رو ترجمه می‌کنه و پس از ترجمه، اون رو اجرا میکنه و سپس میره سراغ خط بعدی.

نکته اینجاست که عملیات ترجمه کد توسط interpreter در هربار اجرای برنامه لازمه انجام بشه.

کدوم بهتره؟

دستنوشته ای از Vaidehi Joshi منتشر شده در مقاله http://yon.ir/IALgU
دستنوشته ای از Vaidehi Joshi منتشر شده در مقاله http://yon.ir/IALgU

خب هردوی اینها مزایای خودشون رو دارن. یک زبان کامپایلری از نظر منطقی سریع تره چون نیاز نیست هر بار عملیات ترجمه کد ما به کد ماشین انجام بشه. همچنین اگر برنامه‌ای به یک زبان کامپایلری نوشته شده باشه لازم نیست کد منبعش رو برای اجرا در اختیار دیگران بزاریم، پس میشه گفت جای سورس اصلی برنامه امن تره!

از طرفی یک زبان مفسری به شدت انعطاف پذیر تر از یک زبان کامپایلری هست. سرعت توسعه بالاتری داره، مدیریت خطاها و... در حین توسعه ساده تره و در کل توسعه آسان تری داره.

نکته مهم: مفسری یا کامپایلری بودن جزئی از ذات یک زبان نیست!!

اینجا، جاییه که خیلی از توسعه دهنده های ایرانی دچار اشتباه میشن. این که میگیم زبان C++ یک زبان کامپایلری هست یا فلان زبان مفسری هست، درواقع به معروف ترین پیاده سازی اون زبان اشاره داره. وگرنه ما میتونیم C++ مفسری (مثل ch) و یا پایتون کامپایلری داشته باشیم. پس اگر بگیم جاوااسکرپیت مفسری یا کامپایلری هست منظورمون اینه که معروف ترین پیاده سازی های این زبان به شکل مفسری عمل می‌کنن!

خب حالا بالاخره، جاوااسکریپت مفسری است یا کامپایلری؟

+ یکی از حضار: مفسریه دیگه همه میدونن!

اکثرا شنیدیم جاوااسکریپت مفسریه. اما واقعا نمیشه این موضوع رو با قطعیت گفت. روشی که موتور های پردازش جاوااسکریپت مثل V8 کد ما رو پردازش می‌کنن واقعا به روش کامپایلر شبیهه. مواردی مثل hoisting یا lexical Scoping یا حتی کلوژرها این شباهت رو بیشتر هم میکنن. (اگر نمیدونید اینا چی هستن مشکلی نیست و ضربه ای به درک شما از این مقاله نمیزنه. اما بهتره توی گوگل جستجو کنید و راجع بهشون بیشتر بخونید چون مباحث مهمی هستن و مقالات خوب و روانی هم در موردشون وجود داره)

به‌علاوه میشه گفت، موتور V8 (پر استفاده ترین موتور پردازش جاوااسکرپیت که توسط گوگل برای مرورگر کروم و با زبان ++C ساخته شد و بعدها توسط NodeJS مورد استفاده قرار گرفت) عملا تمام کد رو ابتدا تبدیل به کد ماشین می‌کنه و سپس اون رو اجرا می‌کنه. تازه این موتور برای شرایط مختلف از چند نوع متفاوتِ ترجمه کد منبع به کد ماشین استفاده میکنه. (توضیح دقیق این نحوه پردازش البته از حوصله این مقاله خارجه.)

+ پس جاوااسکریپت شد کامپایلری دیگه؟

نه! موتورهایی مثل V8 علیرغم روش فوق العاده شون، هیچ وقت یک فایل قابل اجرا به ما تحویل نمیدن و تفاوت هایی از این دست باعث میشه نتونیم به قطعیت بگیم جاوااسکریپت یک زبان کامپایلریه.

+ خب پس چی شد بالاخره؟

اگر گیج شدید و حس می‌کنید نهایتا متوجه نشدید جاوااسکریپت مفسریه یا کامپایلری باید بدونید شما تنها نیستید! بزرگترین متخصصان جاوااسکریپت هم درباره این مسئله نظرات متفاوتی دارند؛ کایل سیمپسون، نویسنده مجموعه کتابهای "شما جاوااسکریپت را نمی‌شناسید" که بیش 108 هزار ستاره در گیت هاب دریافت کرده معتقده که جاوااسکریپت زبانی کامپایلریه (یا حداقل زبانی مفسری نیست). این نقل قول رو ببینید:

فرض کنید برنامه جاوااسکریپتی شما یک سینکس ارور در خط 7 دارد و شما یک alert یا console.log در خط اول دارید. اگر جاوااسکریپت یک زبان مفسری بود، شما باید اول alert یا console.log را مشاهده می‌کردید قبل از اینکه به سینتکس ارور برخورد کنید، درسته؟
اما این اتفاق نمی‌افته! در عوض جاوااسکریپت به شما یک ارور نشون میده قبل از اجرای حتی یک خط کد!

از طرف دیگه شما افراد زیادی رو می‌بینید که هنوز هم معتقدن، جاوااسکریپت یک زبان مفسری هست. میشه گفت اینجا تفاوت اصلی در نگاه افراد به تعریف واقعی کامپایلر/مفسر هست.

و اما نتیجه:

من به شخصه نه میتونم جاوااسکریپت رو کامپایلری به حساب بیارم و نه مفسری. اما یک نکته مهمی رو میدونم که:

با پیشرفت تکنولوژی مرز بین خیلی از چیزهای مختلف از بین میره. زمانی که JIT Compiler ها(مترجمی که عملیات کامپایل رو در لحظه روی کد منبع انجام میداد. چیزی بین مفسر و کامپایلر) ساخته شدن، یا حتی زمانی که موتور های مدرن پردازش جاوااسکریپت بر مبنای JIT ساخته شدن، هیچ کس نمیخواست خودش رو مقید کنه که به کامپایلری بودن یا مفسری بودن وفادار باشه. بلکه همه به دنبال افزایش کارایی و سرعت توسعه و ... بودن. پس ما هم لازم نیست خودمون رو مقید کنیم!

تنها چیزی که درآخر میتونم بگم اینه که اگر به جای صفر و یکی فکر کردن، فاصله بین یک زبان کامپایلری و یک زبان مفسری رو یک بازه فرض کنیم، احتمالا جاوااسکریپت جایی در اواسط اون بازه است. جایی که بهترین کارایی و بیشترین منافع رو داشته باشه. و اینکه به کدوم نزدیک تره، خیلی مهم نیست!!

</نوشتار>



<پی_نوشت>

خب! بالاخره تموم شد! ممنون که تا اینجا همراه من بودید.

با توجه به تحقیق از چند منبع خارجی، سعی در بهتر رساندن مطلب و... من سه چهار ساعت برای نوشتن همین مقاله ساده وقت گذاشتم. (خدا به خیر بگذرونه مطالب سخت تر رو!).

از طرفی سایت های ایرانی راجع به خیلی از مفاهیم بنیادی JS هیچ مطلبی ندارن و اون مطالب موجود هم اصلا و ابدا موضوع رو دقیق بررسی نمی‌کنن(معمولا). مثلا از 10 منبع 8 تاشون میگن کال بک تابعیه که بعد از تموم شدن کار یه تابع دیگه اجرا میشه و تمام! این یعنی خیلی از توسعه دهنده های ما، به واقعیت و پشت صحنه تکنولوژی های مورد استفاده شون تسلط ندارن.

نکته مهم اینجاست که از نظر من جاوااسکریپت مطالب زیادی داره که درست فهمیده نشدن و نمیشن (کال بک ها، کلوژر ها، async ،lexicalScoping و خیلی چیزهای دیگه!) و قصد دارم در یک دنباله راجع به همه شون بنویسم تا در کنار هم معنا و کاربرد واقعی اون ها رو بفهمیم.

ازتون میخوام لطف کنید و در رابطه با این مقاله نظر بدید. لحنش، محتواش و اینکه اصلا مطالبش اون قدر که من فکر میکنم مهم بود یا نه. این باعث میشه بتونم بهتر درباره ادامه این دنباله تصمیم بگیرم.

</پی_نوشت>