~9 دقیقه مطالعه • بروزرسانی ۲۲ مهر ۱۴۰۴
مقدمه: تو هنوز JS را نمیشناسی
فصل اول کتاب You Don’t Know JS Yet با اعترافی جسورانه آغاز میشود: هیچکس جاوااسکریپت را بهطور کامل نمیشناسد. اما این نقص نیست — بلکه دعوتیست به کنجکاوی. شناخت JS مقصد نیست، مسیر است. این فصل، آغاز سفریست با صبر و عمق.
جاوااسکریپت، جاوا نیست
یکی از رایجترین تصورات اشتباه در برنامهنویسی این است که جاوااسکریپت نسخهای از جاواست. اینطور نیست. نام «جاوااسکریپت» حاصل تصمیمی تبلیغاتی در روزهای آغازین وب بود تا توجه برنامهنویسان جاوا را جلب کند. زبان ابتدا Mocha نام داشت، سپس LiveScript و در نهایت «جاوااسکریپت» شد.
اگرچه شباهتهایی در نحو وجود دارد — مانند استفاده از {} برای بلوکها و ; برای پایان دستورات — اما JS و Java اساساً متفاوتاند. همانطور که Jeremy Keith گفته: «جاوا نسبت به جاوااسکریپت مثل ژامبون نسبت به همستر است.»
JS، ECMAScript و استانداردها
برای درک تکامل JS، باید بدانیم که این زبان بهطور رسمی با نام ECMAScript توسط کمیته TC39 تحت نظارت ECMA International استانداردسازی شده است. از سال ۲۰۱۶، نسخههای زبان با سال مشخص میشوند — مثلاً ES2019 — و موتورهای JS در مرورگرها و Node.js این استانداردها را پیادهسازی میکنند.
استفاده از اصطلاحاتی مانند “JS6” یا “ES8” توصیه نمیشود، زیرا باعث سردرگمی میشود. بهتر است از “JS” یا “ES20xx” استفاده کنیم.
کمیته TC39 و استاندارد ECMAScript
مدیریت رسمی زبان جاوااسکریپت بر عهدهٔ کمیتهای به نام TC39 است که زیر نظر سازمان ECMA فعالیت میکند. این کمیته متشکل از ۵۰ تا ۱۰۰ نفر از شرکتهای فعال در حوزهٔ وب مانند Mozilla، Google، Apple و Samsung است. اعضای TC39 بهصورت داوطلبانه فعالیت میکنند، اگرچه بسیاری از آنها کارمندان همین شرکتها هستند.
TC39 هر دو ماه یکبار جلسه برگزار میکند و پیشنهادهای جدید را در قالب فرآیندی ۵ مرحلهای (از مرحله ۰ تا ۴) بررسی میکند. فقط پیشنهادهایی که به مرحله ۴ برسند، در نسخهٔ بعدی استاندارد ECMAScript گنجانده میشوند.
نسخههای رسمی و افسانهٔ چندنسخهای بودن JS
برخلاف تصور رایج، امروزه فقط یک نسخهٔ رسمی از JS وجود دارد — همان نسخهای که توسط TC39 و ECMA استاندارد شده است. در گذشته، نسخههایی مانند JScript توسط مایکروسافت وجود داشتند، اما آن دوران به پایان رسیده است. همهٔ مرورگرها و موتورهای JS متعهد به پیروی از همین استاندارد واحد هستند.
وب، فرمانروای واقعی JS
اگرچه JS در محیطهای مختلفی اجرا میشود (مرورگر، Node.js، دستگاهها، رباتها)، اما واقعیت اصلی JS همان چیزیست که در مرورگرها اجرا میشود. گاهی رفتارهای مشخصشده در استاندارد با رفتارهای واقعی موتورهای مرورگر متفاوتاند، زیرا این موتورها برای حفظ سازگاری با محتوای وب قدیمی، از تغییرات خاصی اجتناب میکنند.
در چنین مواردی، TC39 گاهی استاندارد را با واقعیت وب تطبیق میدهد — مانند تغییر نام contains() به includes() یا بحران معروف smooshgate که منجر به تغییر flatten() به flat() شد.
ضمیمه B – ویژگیهای اضافی برای مرورگرها
برای تطبیق با رفتارهای خاص مرورگرها، استاندارد ECMAScript بخشی به نام Appendix B دارد که شامل ویژگیهاییست که فقط در محیط وب مجازند. این ویژگیها شامل مواردی مانند:
- اعداد هشتهشتی با پیشوند ۰
- توابع
escape()وunescape() - متدهای قدیمی رشته مانند
anchor()وblink() - متد
compile()در RegExp
برخی از این ویژگیها در حالت strict باعث خطاهای اولیه میشوند و بهتر است برای آیندهنگری از آنها اجتناب شود.
آیا alert() جزو JS است؟
تابع alert("Hello, JS!") از نظر نحوی JS است، اما در استاندارد JS تعریف نشده. این تابع و مواردی مانند console.log() یا fetch() جزو APIهای محیطی هستند که توسط مرورگر یا Node.js در اختیار JS قرار میگیرند. آنها مهمانان JS هستند، نه اعضای رسمی آن.
بیشتر تفاوتهایی که کاربران بهعنوان «ناسازگاری JS» مطرح میکنند، در واقع ناشی از تفاوت در
کنسول توسعهدهنده: دقیقاً JS نیست
کنسول مرورگر محیطی دوستانه برای JS است، اما دقیقاً مطابق با رفتار موتور JS در فایلهای .js عمل نمیکند. این ابزار برای آزمایش سریع طراحی شده، نه اجرای دقیق استاندارد. بنابراین، چند نکتهٔ مهم وجود دارد:
- دامنهٔ سراسری: تعریف
varیاfunctionممکن است متغیر سراسری ایجاد کند و باwindowهمگام شود، اما همیشه قابل پیشبینی نیست. - تعریف چندبارهٔ
let/const: در سطح بالا ممکن است خطا ایجاد کند، برخلافvar. - حالت strict: نوشتن
"use strict";در یک خط لزوماً حالت strict را برای کل نشست فعال نمیکند. - this در حالت غیر strict: ممکن است به شیء سراسری اشاره کند، اما محتویات آن همیشه مطابق انتظار نیست.
- Hoisting: در ورودیهای چندخطی کنسول ممکن است متفاوت از فایلهای JS عمل کند.
در نتیجه، کنسول را بهعنوان محیطی دوستانه برای JS ببینید، نه مرجع نهایی رفتار زبان.
سبکهای متنوع در جاوااسکریپت
جاوااسکریپت یک زبان چندسبکی است و از سبکهای مختلف برنامهنویسی پشتیبانی میکند:
- رویهای: کد بهصورت خطی و گامبهگام اجرا میشود.
- شیگرا: داده و منطق در قالب کلاسها و اشیاء سازماندهی میشوند.
- تابعی: کد از توابع خالص و ترکیبپذیر تشکیل شده است.
JS به شما اجازه میدهد این سبکها را ترکیب کنید و برای هر مسئله بهترین رویکرد را انتخاب نمایید.
سازگاری با گذشته در برابر آینده
یکی از اصول بنیادین JS، سازگاری با گذشته است. یعنی کدی که زمانی معتبر بوده، در آینده نیز باید معتبر باقی بماند. شعار TC39 این است: «ما وب را نمیشکنیم.»
این اصل باعث میشود کدی که در سال ۱۹۹۵ نوشته شده، هنوز هم اجرا شود. اما این تعهد، تصمیمگیری برای افزودن یا تغییر ویژگیها را بسیار دشوار میکند، زیرا هر ویژگی جدید عملاً دائمی میشود.
تغییرات ناسازگار با گذشته بسیار نادرند و تنها پس از تحلیل دقیق و اجماع بین مرورگرها انجام میشوند.
چرا JS با آینده سازگار نیست
برخلاف HTML و CSS، جاوااسکریپت با آینده سازگار نیست. یعنی استفاده از ویژگیهای جدید در موتورهای قدیمی باعث خطا میشود. این بهدلیل ماهیت دستوری زبانهای برنامهنویسی است — نمیتوان بهسادگی از روی دستورات ناشناخته عبور کرد.
در مقابل، HTML و CSS زبانهایی اعلامی هستند و مرورگرها میتوانند ویژگیهای ناشناخته را نادیده بگیرند بدون اینکه بقیهٔ صفحه آسیب ببیند.
پرش از شکافها: ترنسپایل و سازگاری با مرورگرهای قدیمی
جاوااسکریپت با نسخههای قدیمی سازگار نیست (forwards-compatible نیست). یعنی استفاده از ویژگیهای جدید مانند ES2019 در موتورهای قدیمی ممکن است باعث خطا شود. برای رفع این مشکل، ابزارهایی مانند Babel کد مدرن را به نسخههای قدیمیتر تبدیل میکنند. این فرآیند ترنسپایل نام دارد.
مثلاً let به var تبدیل میشود تا در مرورگرهای قبل از ES6 قابل اجرا باشد. این ابزارها به توسعهدهندگان اجازه میدهند کد خوانا و مدرن بنویسند و در عین حال با مرورگرهای قدیمی سازگار باقی بمانند.
پر کردن شکافها: پلیفیل برای APIهای جدید
برای ویژگیهایی که در موتورهای قدیمی وجود ندارند (مانند Promise.prototype.finally) از پلیفیل استفاده میشود. پلیفیلها نسخهای شبیهسازیشده از APIهای جدید هستند که در محیطهای قدیمی اجرا میشوند.
مثال:
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
return this.then(
value => Promise.resolve(callback()).then(() => value),
reason => Promise.resolve(callback()).then(() => { throw reason; })
);
};
}ابزارهایی مانند Babel معمولاً پلیفیلهای لازم را بهصورت خودکار اضافه میکنند، اما گاهی نیاز به درج دستی آنها داریم.
تفسیر یا کامپایل؟
اگرچه JS بهعنوان زبان اسکریپتی شناخته میشود، اما در عمل کامپایلشده است. موتورهای JS ابتدا کد را به AST (درخت نحوی) تبدیل میکنند، سپس آن را به بایتکد کامپایل کرده و با استفاده از JIT بهینهسازی میکنند.
این فرآیند باعث میشود خطاهای نحوی مانند پارامترهای تکراری قبل از اجرا شناسایی شوند، که رفتاری مشابه زبانهای کامپایلشده است.
WebAssembly: مکملی برای JS
برای بهبود عملکرد، ابتدا ASM.js معرفی شد — زیرمجموعهای از JS با راهنماییهای نوعی برای اجرای سریعتر. سپس WASM آمد: فرمتی باینری که قبل از اجرا کامپایل میشود و بسیار سریعتر از JS اجرا میشود.
WASM به زبانهایی مانند C و Go اجازه میدهد در موتور JS اجرا شوند. این فناوری مکمل JS است، نه جایگزین آن. ابزارهایی مانند AssemblyScript تلاش میکنند JS را به WASM نزدیکتر کنند، اما JS بهدلیل نوعگرایی پویا برای WASM مناسب نیست.
حالت سختگیرانه (Strict Mode)
در ES5 معرفی شد و با نوشتن "use strict"; در ابتدای فایل یا تابع فعال میشود. این حالت باعث جلوگیری از خطاهای رایج و بهینهسازی بهتر میشود. مثلاً در حالت strict، this در توابع بدون شیء به undefined اشاره میکند.
اگر قبل از "use strict"; یک ; قرار گیرد، ممکن است حالت سختگیرانه فعال نشود. بنابراین بهتر است از حالت فایلمحور استفاده کنیم، نه تابعمحور.
JS: زبان چندسبکی
جاوااسکریپت از سبکهای مختلف برنامهنویسی پشتیبانی میکند:
- رویهای: اجرای خطی و گامبهگام
- شیگرا: استفاده از کلاسها و اشیاء
- تابعی: ترکیب توابع خالص و قابلتغییر
JS به شما اجازه میدهد این سبکها را ترکیب کرده و برای هر مسئله بهترین رویکرد را انتخاب کنید.
سازگاری با گذشته در برابر آینده
JS با گذشته سازگار است — یعنی کدی که در سال ۱۹۹۵ نوشته شده، هنوز هم باید اجرا شود. این اصل باعث میشود JS انتخابی امن برای پروژههای بلندمدت باشد. اما JS با آینده سازگار نیست: استفاده از ویژگیهای جدید در موتورهای قدیمی باعث خطا میشود.
برخلاف HTML و CSS که اعلامی هستند و مرورگرها میتوانند ویژگیهای ناشناخته را نادیده بگیرند، JS زبان دستوری است و نمیتوان از روی دستورات ناشناخته عبور کرد.
جمعبندی
جاوااسکریپت زبانیست پویا، چندسبکی و کامپایلشده که با ابزارهایی مانند ترنسپایلر و پلیفیل قابلسازگاری با محیطهای مختلف است. WASM مکملی برای JS است، نه جایگزین آن. حالت سختگیرانه به بهینهسازی و کاهش خطا کمک میکند، و اصل سازگاری با گذشته JS را به پایهای پایدار برای وب تبدیل کرده است.
نوشته و پژوهش شده توسط دکتر شاهین صیامی