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