جاوااسکریپت هرگز قرار نبود به آنچه امروز هست تبدیل شود. در سال ۱۹۹۵، برندن ایچ در Netscape نشست و در عرض ده روز یک نمونه اولیه را آماده کرد. ده روز. این زمان به سختی برای نوشتن یک مشخصات فنی مناسب کافی است، چه برسد به طراحی یک زبان برنامهنویسی که قرار است قدرت بخشیدن به میلیاردها دستگاه باشد. نتیجه، زبانی با ویژگیهای عجیب و غریب بود که توسعهدهندگان هنوز هم پس از گذشت نزدیک به سه دهه، در آنها دچار مشکل میشوند. با این حال، همان خلقِ شتابزده، به پرکاربردترین runtime در تاریخ نرمافزار تبدیل شد. این موفقیت نه به دلیل ظرافت طراحی، بلکه به این دلیل بود که دقیقاً در لحظهای که وب به آن نیاز داشت، درون مرورگر عرضه شد.
متولد شده در عجله
جنگهای مرورگر در اواسط دهه نود رقابتی دوستانه نبودند. Netscape به یک زبان اسکریپتنویسی سبک نیاز داشت که بتواند در کنار Java در مرورگر Navigator اجرا شود. وظیفه ایچ این بود که چیزی بسازد که به اندازه کافی شبیه Java به نظر برسد تا مدیران را راضی کند، اما در عین حال به قدری ساده باشد که افراد غیربرنامهنویس بتوانند آن را در صفحات وب کپی کنند. ضربالاجل تعیین شده مضحک بود. او Mocha را تولید کرد که به زودی به LiveScript و در نهایت به عنوان یک حرکت بازاریابی برای سوار شدن بر موج محبوبیت Java، به JavaScript تغییر نام یافت.
آن تولد شتابزده، آسیبهای دائمی به جا گذاشت. تبدیل نوع (Type coercion) هنوز هم وقتی عملگر + بدون هشدار، رشتهها و اعداد را به هم میچسباند، تازهواردها را گیج میکند. typeof null به دلیل باگی در پیادهسازی اولیه که هیچکس جرأت اصلاح آن را ندارد (از ترس از کار افتادن وب)، مقدار "object" را برمیگرداند. درج خودکار سمیکولون (Automatic semicolon insertion) باعث بروز خطاهای بیصدا میشود. متغیرهایی که با var تعریف میشوند، به گونهای در محدوده (scope) نشت میکنند که غیرقابل پیشبینی به نظر میرسند. اینها نقصهای طراحی انتزاعی نیستند؛ بلکه کلافگیهای روزمرهای هستند که مستقیماً به آن دویِ دو هفتهای در مه ۱۹۹۵ بازمیگردند.
تبار عجیب
اگر به دقت به JavaScript نگاه کنید، سه دودمان متمایز را خواهید دید که به هم دوخته شدهاند. نحو (syntax) این زبان به شدت از Java وام گرفته شده است. آکولادها، دستورات if و حلقههای for شکلی آشنا برای هر کسی که از زبانهای سبک C میآید، فراهم میکنند. اما در زیر این سطح، رفتار زبان کاملاً متفاوت است.
قلب محاسباتی واقعی این زبان از Scheme میآید که گویشی از Lisp است. این همان جایی است که JavaScript توابع درجه اول (first-class functions) را به دست آورد؛ به این معنی که تو函数 میتوانند به عنوان آرگومان ارسال شوند، از توابع دیگر بازگردانده شوند و به متغیرها اختصاص یابند. همچنین این منبع، کلوژرها (closures) را به ما داد که اجازه میدهند یک تابع داخلی حتی پس از پایان اجرای تابع بیرونی، به محدوده (scope) آن دسترسی داشته باشد. اگر تا به حال یک callback نوشتهاید یا یک event listener را متصل کردهاید، از DNA به ارث رسیده از Scheme استفاده کردهاید.
سپس مدل شیءگرا (object model) قرار دارد که از Self آمده است. JavaScript به جای وراثت کلاسیک با کلاسهای صلب، از پروتوتایپها (prototypes) استفاده میکند. یک شیء میتواند مستقیماً به شیء دیگری متصل شود و جستجوی ویژگیها را به بالا ارجاع دهد. شما میتوانید با استفاده از Object.create یک شیء بسازید و بدون اینکه هرگز کلاسی تعریف کنید، زنجیرههایی ایجاد کنید. JavaScript مدرن کلمه کلیدی class را اضافه کرده است، اما این عمدتاً یک syntactic sugar روی همان مکانیزم پروتوتایپ زیرین است.
از تزئین صفحه تا ابزاری جدی
در چند سال اول، JavaScript کارهای کوچکی انجام میداد. ورودیهای فرم را قبل از ارسال به سرور اعتبارسنجی میکرد. تصاویر را هنگام نگه داشتن ماوس روی آنها عوض میکرد. این یک اسباببازی بود، نه یک ابزار. پیادهسازیهای مرورگرها ناهماهنگ بود، بنابراین توسعهدهندگان اغلب مسیرهای کد متفاوتی برای Netscape و Internet Explorer مینوشتند.
استانداردسازی از طریق ECMAScript این مسیر را تغییر داد. این مشخصات فنی، هدف مشترکی را برای سازندگان مرورگر جهت پیادهسازی فراهم کرد که به آرامی بدترین ناسازگاریها را برطرف کرد. سپس Ajax از راه رسید.
Ajax که مخفف Asynchronous JavaScript and XML است، یک فناوری جدید واحد نبود، بلکه الگویی بود که قطعات موجود را با هم ترکیب میکرد. جزء حیاتی، شیء XMLHttpRequest بود که به مرورگر اجازه میداد بدون بارگذاری مجدد کل صفحه، در پسزمینه از سرور درخواست داده شود. وقتی Google در سال ۲۰۰۵ Maps و در سال ۲۰۰۴ Gmail را راهاندازی کرد، کاربران ناگهان پاسخگویی مشابه دسکتاپ را در داخل یک تب مرورگر تجربه کردند. صفحات وب به اپلیکیشن تبدیل شدند. JavaScript دیگر یک تزیین نبود؛ بلکه غذای اصلی بود.
سرعت و جاهطلبی
عملکرد خام زمانی بزرگترین شوخی JavaScript بود. مفسرهای اولیه کند بودند. سپس Google در سال ۲۰۰۸ موتور V8 را در کنار Chrome منتشر کرد و دیگر این شوخی خندهدار نبود. V8 قابلیت just-in-time compilation را معرفی کرد که JavaScript را به جای تفسیر خط به خط، در زمان اجرا به کد ماشین تبدیل میکرد. این موتور همچنین hidden classes و inline caching را معرفی کرد تا دسترسی به ویژگیها را حتی در اشیاء پویا سریع کند. سایر مرورگرها با موتورهای پرسرعت خود واکنش نشان دادند و زبان ناگهان برای محاسبات واقعی به اندازه کافی سریع شد.
آن سرعت، تحول بعدی را ممکن ساخت. رایان دال در سال ۲۰۰۹ Node.js را منتشر کرد؛ او V8 را از مرورگر جدا کرد و آن را در یک مدل ورودی-خروجی غیرمسدودکننده (non-blocking) و رویداد-محور (event-driven) بستهبندی کرد. وبسرورها پیش از این برای هر درخواست ورودی، یک رشته (thread) جدید ایجاد میکردند که این امر در مواجهه با همزمانی (concurrency) بالا، باعث فروپاشی سیستم میشد. Node.js با استفاده از یک حلقه رویداد (event loop) و فراخوانهای ناهمگام (asynchronous callbacks)، دهها هزار اتصال همزمان را تنها در یک رشته مدیریت میکرد. JavaScript از سمت کلاینت به سمت سرور، ابزارهای ساخت (build tools) و در نهایت به هر چیز دیگری سرازیر شد.
زبانی که همهجا هست
اکنون JavaScript در جاهایی اجرا میشود که خالق آن هرگز تصور نمیکرد.
در بخش فرانتاند، React و Vue به نحوه ساخت رابطهای کاربری مدرن شکل میدهند. کامپوننتها در پاسخ به تغییرات وضعیت (state)، بدون اینکه مرورگر نیاز به بارگذاری مجدد و سنگین کل صفحه داشته باشد، بهروزرسانی میشوند. در بخش بکاند، Node.js قدرتبخش APIها و سرویسهای بلادرنگ (real-time) است، در حالی که محیطهای اجرایی (runtimes) جدیدتر مانند Bun در حال آزمایش مدیریت بستههای سریعتر و باندلینگ (bundling) داخلی هستند.
React Native کدهای JavaScript را به نماهای بومی (native) پلتفرم ترجمه میکند و به تیمها اجازه میدهد بدون نیاز به نگهداری دو پایگاه کد کاملاً مجزا در Swift و Kotlin، اپلیکیشنهای موبایل برای iOS و Android عرضه کنند. Electron فناوریهای وب را درون یک پوسته Chromium بستهبندی میکند تا نرمافزارهای دسکتاپ ساخته شوند؛ این همان روشی است که Slack و Visual Studio Code به لپتاپ شما میرسند. این زبان حتی در توابع ابری (cloud functions) و ورکرهای محاسبات لبهای (edge computing) نیز حضور دارد و منطق برنامهنویسی را در فاصله چند میلیثانیهای از کاربر نهایی در شبکههای توزیعشده اجرا میکند.
پارادوکس فراوانی
همهجا بودن هزینهای دارد. اکوسیستم بسیار عظیم است و این وسعت، اضطراب ایجاد میکند. پیش از آنکه پیکربندی آخرین ابزار ساخت را تمام کنید، ابزار جدیدی ظاهر میشود. محبوبیت فریمورکها در بازههای زمانیای بالا و پایین میرود که گویی فصلی هستند. شما میتوانید تقریباً هر مشکلی را بدون خروج از دنیای JavaScript حل کنید، اما ابتدا باید از میان دهها راهکار با دیدگاههای بسیار مشخص (highly opinionated) یکی را انتخاب کنید. درختهای وابستگی (dependency trees) عمیق و شکننده میشوند. یک بسته برای افزودن فاصله (padding) به سمت چپ آرایهها، میتواند با ناپدید شدن از مخزن (registry)، هزاران پروژه وابسته را از کار بیندازد.
هیچکدام از اینها تصادفی نیست. JavaScript آشفته است چون وب آشفته است. این زبان، لایههای ارگانیکی از سازگاری با نسخههای قدیمی (backward compatibility)، استانداردهای شتابزده و منافع متضاد در پیادهسازی است. با این حال، همین آشفتگی دلیل قدرتمند بودن این زبان است. وب همهجا هست و چون JavaScript بهطور پیشفرض درون هر مرورگری زندگی میکند، نزدیکترین چیزی است که ما به یک محیط اجرایی جهانی (universal runtime) داریم.
مدتهاست که دیگر یک زبان اسکریپتنویسی ساده نیست. JavaScript اکنون یک پلتفرم جهانی برای نرمافزار است؛ زبانی که در ده روز ساخته شد و با این قرارداد نامرئی در کنار هم نگه داشته شده است که وب نباید آنچه را که قبلاً وجود داشته، از کار بیندازد. اگر زخمهای آن را در کنار نقاط قوتش بیاموزید، در واقع در حال یادگیری تاریخچه خودِ اینترنت مدرن هستید.
