前言
傳統上,JavaScript 是運行在瀏覽器上的命令稿語言。但 JavaScript 早就從瀏覽器上的新玩意兒進化成全功能的通用型語言,在許多情境都可以用 JavaScript 撰寫程式。本文從當代的觀點來看 JavaScript 程式設計。
多樣化的 JavaScript 運行環境
可以使用 JavaScript 撰寫程式的情境包括但不限於:
- 瀏覽器 (browser)
- Chrome
- Safari
- Firefox
- Edge 和 Edge Legacy
- Internet Explorer
- Opera
- 伺服端 (server side scripting)
- Node.js
- Deno
- GraalVM
- Rhino
- 行動端 (mobile software)
- React Native
- Cordova
- NativeScript
- 資料庫 (database)
- MongoDB
- CouchDB
- 遊戲引擎 (game engine)
- RPG Maker
- Cocos Creator
這個清單很長,會讓人不知如何開始。目前 JavaScript 運行環境中,最重要的還是瀏覽器和 Node.js,先從這兩者開始即可。其他的運行環境等有需要時再學,因為新的工具會不斷出現,但是開發者的時間有限。
瀏覽器上的 JavaScript 會重要的原因在於該語言是瀏覽器的實質 (de facto) 標準。絕大部分的前端技術都失敗了,WebAssembly 也不會完全取代 JavaScript。有在寫網頁前端程式的話,JavaScript 仍然是必學的。
在瀏覽器以外最廣泛使用的 JavaScript 運行環境就是 Node.js。除了用來寫網頁後端程式外,Node.js 還可以用來寫命令列工具、嵌入式軟體等。即使不寫 Node.js 程式,還是可以學一下 Node.js 的使用方式,因為許多網頁前端程式的開發工具也是以 Node.js 寫成。
瀏覽器和 Node.js 環境的差異
雖然在寫網頁前端程式和 Node.js 程式時,使用的語言都是 JavaScript,但兩者是不同的環境。其差異在於兩者的 API 及模組 (module) 系統相異。
瀏覽器的 JavaScript 有操作網頁元素的 API。此外,考量安全性,瀏覽器的 JavaScript 以沙箱 (sandbox) 模式執行,不能直接存取系統資源,而由瀏覽器 API 提供必要的功能。
相對來說,Node.js 不搭配瀏覽器,沒有操作網頁元素的 API。由於 Node.js 程式是在本地端執行,可直接存取系統資源,且提供能和系統互動的 API。
原本的 JavaScript 沒有模組的概念,所有的 JavaScript 命令稿讀入後共用全域命名空間。JavaScript 模組是新的特性,較舊的瀏覽器不支援。相對來說,Node.js 以 CommonJS 做為其模組系統。這兩者是不相容的。
所以,即使網頁前端和網頁後端都以 JavaScript 撰寫,不代表程式碼就能共用。
要學 Deno 嗎?
Deno 是 Node.js 的原作者所開發的新興 JavaScript 和 TypeScript 運行環境,其目的是改善 Node.js 的設計缺失。所以,相容於 Node.js 並不是 Deno 的主要目標。
雖然 Deno 的版本號已經過 1.0
了,但 Deno 的生態圈還不成熟,可用的套件還蠻少的。如果手上有一些 side project 要玩玩看新工具無妨,要用在實際上線的專案的話,最好再觀望一陣子比較保險。
以 JavaScript 為編譯目標的語言
JavaScript 是重要的語言,但這個語言本身有一些設計上的缺陷。目前的實務是使用 JavaScript 轉譯器來繞過 JavaScript 本身的不良特性。以下是一些常見的 JavaScript 轉譯器:
- TypeScript
- Babel
- Dart
- Flow
- CoffeeScript
- Elm
- ReasonML
- Opal
- ClojureScript
- Parenscript
同樣地,這個清單很長,會讓人不知如何是好。目前來說,最重要的是 TypeScript 和 Babel (註) 。因為這兩者相容於 JavaScript 本身,不需重新學習完全不同的語法,重構現有的 JavaScript 專案比較容易。
(註) Babel 可單獨使用或搭配 Flow。
為什麼 JavaScript 沒有輸出入的功能?
由於 JavaScript 原先是內嵌在網頁中的命令稿語言,沒有實作終端機輸出入的功能。JavaScript 會從網頁上的表單取得資料,運算後將將資料印在網頁上;這些過程都要由網頁程式設計師自行寫 JavaScript 程式來處理。
Node.js 有實作終端機輸出入的功能,因為 Node.js 運行在瀏覽器外。由於 JavaScript 採用非同步輸出入,在 Node.js 使用輸出入功能會比其他程式語言來得複雜一些。
為什麼 JavaScript 要設計成非同步執行?
因為原本 JavaScript 的功能是為網頁元素寫事件處理器 (event handler)。當頁面的定位為網頁程式的使用者介面時,使用事件導向程式 (event-driven programming) 是相當合適的。這種模式適於非同步執行,不讓特定事件阻塞住整個頁面的運行。
由於 JavaScript 皆是以非同步模式來運行,寫傳統的命令式程式比較困難。後來 JavaScript 發展出 Promise、async
/await
語法等功能,就是為了簡化非同步程式的實作。
本系列文章的方向
在本系列文章中,我們會介紹 JavaScript 的語法特性。有關網頁設計的部分會放在這裡,有關 Node.js 的部分則會放在這裡。