JavaScript ไม่เคยถูกตั้งใจให้กลายเป็นอย่างที่เป็นอยู่ในปัจจุบัน ในปี 1995 Brendan Eich นั่งอยู่ที่ Netscape และปั้นต้นแบบขึ้นมาภายในเวลาเพียงสิบวัน สิบวันเท่านั้น นั่นแทบจะไม่เพียงพอสำหรับการเขียนข้อกำหนด (specification) ที่ดีเสียด้วยซ้ำ อย่าว่าแต่การออกแบบภาษาโปรแกรมที่ต้องรองรับอุปกรณ์หลายพันล้านเครื่องเลย ผลลัพธ์ที่ได้คือภาษาที่มีความแปลกประหลาด (quirks) ซึ่งนักพัฒนายังคงสะดุดอยู่จนถึงปัจจุบันแม้จะผ่านไปเกือบสามทศวรรษแล้วก็ตาม กระนั้น สิ่งที่ถูกสร้างขึ้นอย่างเร่งรีบนี้กลับกลายเป็น runtime ที่ถูกใช้งานแพร่หลายที่สุดในประวัติศาสตร์ซอฟต์แวร์ มันไม่ได้ชนะเพราะความสง่างาม แต่ชนะเพราะมันถูกส่งมอบพร้อมกับเบราว์เซอร์ในจังหวะที่เว็บต้องการมันพอดี
กำเนิดอย่างเร่งรีบ
สงครามเบราว์เซอร์ในช่วงกลางยุคเก้าศูนย์ไม่ใช่การแข่งขันที่เป็นมิตร Netscape ต้องการภาษาสคริปต์ที่มีน้ำหนักเบาซึ่งสามารถทำงานควบคู่ไปกับ Java ในเบราว์เซอร์ Navigator ได้ Eich ได้รับมอบหมายให้สร้างบางสิ่งที่ดูคล้ายกับ Java มากพอที่จะทำให้ผู้บริหารพอใจ แต่ต้องเรียบง่ายพอที่คนที่ไม่ใช่นักโปรแกรมเมอร์จะสามารถคัดลอกไปวางในหน้าเว็บได้ กำหนดการนั้นบ้าบอมาก เขาจึงสร้าง Mocha ขึ้นมา ซึ่งต่อมาเปลี่ยนชื่อเป็น LiveScript และในที่สุดก็กลายเป็น JavaScript เพื่อใช้เป็นกลยุทธ์ทางการตลาดในการเกาะกระแสความนิยมของ Java
การกำเนิดที่เร่งรีบนั้นทิ้งรอยแผลเป็นถาวรไว้ การแปลงประเภทข้อมูล (Type coercion) ยังคงสร้างความสับสนให้กับผู้มาใหม่ เมื่อตัวดำเนินการบวก (+) นำสตริงและตัวเลขมาต่อกันโดยไม่มีการแจ้งเตือน typeof null คืนค่าเป็น "object" เนื่องจากข้อผิดพลาด (bug) ในการ実装 (implementation) ดั้งเดิมที่ไม่มีใครกล้าแก้ไขเพราะกลัวจะทำให้เว็บพัง การใส่เซมิโคลอนโดยอัตโนมัติ (Automatic semicolon insertion) ก่อให้เกิดความล้มเหลวแบบเงียบๆ ตัวแปรที่ประกาศด้วย var มีการรั่วไหลของขอบเขต (scope) ในลักษณะที่คาดเดาได้ยาก สิ่งเหล่านี้ไม่ใช่ข้อบกพร่องในการออกแบบเชิงนามธรรม แต่มันคือความหงุดหงิดในชีวิตประจำวันที่สืบย้อนกลับไปได้โดยตรงถึงการเร่งทำงานแบบสปรินต์สองสัปดาห์ในเดือนพฤษภาคม ปี 1995
บรรพบุรุษที่แปลกประหลาด
หากมอง JavaScript อย่างละเอียด คุณจะเห็นสายเลือดสามสายที่แตกต่างกันถูกนำมาเย็บรวมกัน ไวยากรณ์ (syntax) ของมันหยิบยืมมาจาก Java อย่างหนัก ทั้งเครื่องหมายปีกกา, คำสั่ง if และลูป for ทำให้ดูคุ้นเคยสำหรับใครก็ตามที่มาจากภาษาตระกูล C แต่ภายใต้พื้นผิวนั้น พฤติกรรมของมันกลับแตกต่างกันโดยสิ้นเชิง
หัวใจสำคัญในการคำนวณที่แท้จริงของภาษานี้มาจาก Scheme ซึ่งเป็นภาษาในตระกูล Lisp นี่คือจุดที่ JavaScript ได้รับฟังก์ชันระดับเฟิร์สคลาส (first-class functions) ซึ่งหมายความว่าฟังก์ชันสามารถถูกส่งเป็นอาร์กิวเมนต์, ถูกส่งคืนจากฟังก์ชันอื่น และถูกกำหนดให้กับตัวแปรได้ นอกจากนี้ยังให้ closures แก่เรา ซึ่งช่วยให้ฟังก์ชันภายในสามารถเข้าถึงขอบเขตของฟังก์ชันภายนอกได้ แม้ว่าฟังก์ชันภายนอกนั้นจะทำงานเสร็จสิ้นไปแล้วก็ตาม หากคุณเคยเขียน callback หรือแนบ event listener คุณก็ได้ใช้ DNA ที่สืบทอดมาจาก Scheme แล้ว
ต่อมาคือโมเดลวัตถุ (object model) ซึ่งมาจาก Self แทนที่จะเป็นการสืบทอดแบบคลาสสิก (classical inheritance) ที่มีคลาสที่ตายตัว JavaScript ใช้ prototypes วัตถุหนึ่งสามารถเชื่อมโยงโดยตรงกับวัตถุอื่นและส่งต่อการค้นหาคุณสมบัติ (property lookups) ขึ้นไปด้านบน คุณสามารถสร้างวัตถุด้วย Object.create และสร้างสายโซ่ (chains) ได้โดยไม่ต้องนิยามคลาสเลย JavaScript สมัยใหม่ได้เพิ่มคีย์เวิร์ด class เข้ามา แต่มันเป็นเพียง syntactic sugar ที่ครอบทับกลไก prototype พื้นฐานนี้ไว้เท่านั้น
จากเครื่องประดับหน้าเว็บสู่เครื่องมือที่จริงจัง
ในช่วงไม่กี่ปีแรก JavaScript ทำหน้าที่เพียงเล็กน้อย มันใช้ตรวจสอบข้อมูลในฟอร์มก่อนที่จะส่งไปยังเซิร์ฟเวอร์ ใช้สลับรูปภาพเมื่อเอาเมาส์ไปวาง มันเป็นเพียงของเล่น ไม่ใช่เครื่องมือ การทำงานของเบราว์เซอร์แต่ละเจ้ายังไม่สอดคล้องกัน นักพัฒนาจึงมักต้องเขียนโค้ดแยกกันสำหรับ Netscape และ Internet Explorer
การสร้างมาตรฐานผ่าน ECMAScript ได้เปลี่ยนเส้นทางนั้น ข้อกำหนดมาตรฐานช่วยให้ผู้ผลิตเบราว์เซอร์มีเป้าหมายร่วมกันในการพัฒนา ซึ่งค่อยๆ ช่วยขจัดความไม่เข้ากันที่เลวร้ายที่สุดออกไป จากนั้น Ajax ก็ตามมา
Ajax หรือย่อมาจาก Asynchronous JavaScript and XML ไม่ใช่เทคโนโลยีใหม่เพียงอย่างเดียว แต่เป็นรูปแบบ (pattern) ที่รวมเอาส่วนประกอบที่มีอยู่เดิมเข้าด้วยกัน ส่วนประกอบสำคัญคือวัตถุ XMLHttpRequest ซึ่งช่วยให้เบราว์เซอร์สามารถขอข้อมูลจากเซิร์ฟเวอร์ในเบื้องหลังได้โดยไม่ต้องโหลดหน้าเว็บใหม่ทั้งหมด เมื่อ Google เปิดตัว Maps ในปี 2005 และ Gmail ในปี 2004 ผู้ใช้ก็สัมผัสได้ถึงการตอบสนองที่รวดเร็วเหมือนใช้งานบนเดสก์ท็อปภายในแท็บเบราว์เซอร์ หน้าเว็บจึงกลายเป็นแอปพลิเคชัน JavaScript ไม่ใช่แค่เครื่องประดับอีกต่อไป แต่มันคืออาหารจานหลัก
ความเร็วและความทะเยอทะยาน
ประสิทธิภาพดิบ (Raw performance) เคยเป็นเรื่องตลกที่ใหญ่ที่สุดของ JavaScript อินเทอร์พรีเตอร์ (interpreters) ยุคแรกนั้นช้ามาก จนกระทั่ง Google ปล่อย V8 engine ในปี 2008 พร้อมกับ Chrome เรื่องตลกนั้นก็ไม่ขำอีกต่อไป V8 ได้นำการคอมไพล์แบบ just-in-time มาใช้ โดยการแปล JavaScript เป็น machine code ในขณะที่โปรแกรมทำงาน (runtime) แทนที่จะเป็นการตีความ (interpret) ทีละบรรทัด นอกจากนี้ยังมีการนำ hidden classes และ inline caching มาใช้เพื่อให้การเข้าถึงคุณสมบัติทำได้อย่างรวดเร็วแม้ในวัตถุแบบไดนามิก เบราว์เซอร์อื่นๆ จึงตอบโต้ด้วย engine ความเร็วสูงของตนเอง และทันใดนั้นภาษานี้ก็มีความเร็วเพียงพอสำหรับการคำนวณที่แท้จริง
ความเร็วระดับนั้นได้เปิดทางไปสู่การเปลี่ยนแปลงครั้งถัดไป Ryan Dahl ได้ปล่อย Node.js ในปี 2009 โดยการดึง V8 ออกจากเบราว์เซอร์แล้วนำมาห่อหุ้มด้วยโมเดล input-output แบบ event-driven และ non-blocking เว็บเซิร์ฟเวอร์ในสมัยก่อนจะสร้าง thread ใหม่สำหรับทุกคำขอที่เข้ามา ซึ่งทำให้ระบบล่มเมื่อต้องรับมือกับ concurrency จำนวนมาก แต่ Node.js สามารถจัดการการเชื่อมต่อพร้อมกันได้หลายหมื่นรายการบน single thread เพียงหนึ่งเดียว โดยใช้ event loop และ asynchronous callbacks ทำให้ JavaScript ขยายขอบเขตจากฝั่ง client ไปสู่ฝั่ง server เข้าสู่ build tools และในที่สุดก็แทรกซึมไปสู่ทุกสิ่งทุกอย่าง
ภาษาที่อยู่ทุกหนทุกแห่ง
ปัจจุบัน JavaScript ทำงานในสถานที่ที่ผู้สร้างของมันไม่เคยจินตนาการถึงมาก่อน
ในฝั่ง frontend, React และ Vue เป็นตัวกำหนดรูปแบบการสร้าง interface สมัยใหม่ Component ต่างๆ จะอัปเดตตามการเปลี่ยนแปลงของ state โดยที่เบราว์เซอร์ไม่ต้องทำการโหลดหน้าเว็บใหม่ทั้งหมดซึ่งสิ้นเปลืองทรัพยากร ในฝั่ง backend, Node.js ขับเคลื่อน APIs และบริการแบบ real-time ในขณะที่ runtime รุ่นใหม่อย่าง Bun กำลังทดลองเรื่องการจัดการ package ที่รวดเร็วขึ้นและการทำ bundling ในตัว
React Native แปลงโค้ด JavaScript ให้เป็น native platform views ช่วยให้ทีมพัฒนาสามารถส่งมอบแอปพลิเคชันมือถือสำหรับ iOS และ Android ได้โดยไม่ต้องดูแล codebase สองชุดที่แยกจากกันโดยสิ้นเชิงในภาษา Swift และ Kotlin Electron นำเทคโนโลยีเว็บมาห่อหุ้มไว้ใน Chromium shell เพื่อสร้างซอฟต์แวร์เดสก์ท็อป ซึ่งเป็นวิธีที่ทั้ง Slack และ Visual Studio Code เข้ามาอยู่บนแล็ปท็อปของคุณ ภาษาดังกล่าวยังฝังตัวอยู่ใน cloud functions และ edge computing workers โดยประมวลผล logic ห่างจากผู้ใช้งานปลายทางเพียงไม่กี่มิลลิวินาทีผ่านเครือข่ายแบบกระจายตัว (distributed networks)
ความย้อนแย้งของความล้นเหลือ
ความแพร่หลายย่อมมีราคาที่ต้องจ่าย ระบบนิเวศ (ecosystem) นั้นใหญ่โตมหาศาล และขนาดที่ใหญ่โตนี้ก็นำมาซึ่งความวิตกกังวล เครื่องมือ build ตัวใหม่ปรากฏขึ้นก่อนที่คุณจะตั้งค่าตัวเก่าเสร็จเสียด้วยซ้ำ Framework ต่างๆ มีความนิยมขึ้นและลงในระยะเวลาที่ให้ความรู้สึกเหมือนเป็นเรื่องตามฤดูกาล คุณสามารถแก้ปัญหาเกือบทุกอย่างได้โดยไม่ต้องออกจากโลกของ JavaScript แต่ก่อนอื่นคุณต้องเลือกระหว่างโซลูชันที่มีแนวทางเฉพาะตัว (highly opinionated) นับสิบแบบ Dependency trees มีความซับซ้อนและเปราะบาง แพ็กเกจสำหรับ left-padding arrays เพียงตัวเดียวอาจทำให้โปรเจกต์ปลายทาง (downstream projects) นับพันพังทลายลงได้หากมันหายไปจาก registry
ทั้งหมดนี้ไม่ใช่เรื่องบังเอิญ JavaScript นั้นยุ่งเหยิงเพราะเว็บก็ยุ่งเหยิง มันคือชั้นเค้กที่ประกอบขึ้นอย่างเป็นธรรมชาติจาก backward compatibility, มาตรฐานที่ถูกเร่งรีบ และผลประโยชน์ในการนำไปใช้งานที่แข่งขันกัน ทว่าความยุ่งเหยิงแบบเดียวกันนี้เองที่ทำให้ภาษานี้ทรงพลัง เว็บอยู่ทุกหนทุกแห่ง และเนื่องจาก JavaScript ฝังอยู่ในทุกเบราว์เซอร์โดยค่าเริ่มต้น มันจึงเป็นสิ่งที่ใกล้เคียงที่สุดกับ universal runtime ที่เรามี
มันเลิกเป็นเพียงแค่ scripting language ไปนานแล้ว ปัจจุบัน JavaScript คือแพลตฟอร์มระดับโลกสำหรับซอฟต์แวร์ ที่ถูกสร้างขึ้นในเวลาเพียงสิบวัน และยึดโยงเข้าด้วยกันด้วยสัญญาที่มองไม่เห็นว่า เว็บจะต้องไม่ทำลายสิ่งที่เคยมีมาก่อนหน้า หากคุณเรียนรู้รอยแผลเป็นไปพร้อมกับจุดแข็งของมัน คุณก็กำลังเรียนรู้ประวัติศาสตร์ของอินเทอร์เน็ตสมัยใหม่นั่นเอง
