前言
我們先前分別簡介過 HTML 和 CSS,在本文中,我們介紹一下 JavaScript。由於 JavaScript 的內容可以出一整本書 (像 JavaScript 大全第六版超過 1,000 頁),這裡僅止於一些概念上的介紹。
網頁前端技術的過去和現在
原先網頁僅是靜態的文件,JavaScript 是後來才加上去的技術。JavaScript 主要的目的就是在網頁中加上程式邏輯。在網頁技術長年的發展中,並不是沒有其他的前端技術,但這些技術最後都沒有成功,以下是一些實例:
- Java Applet
- Flash
- ActiveX
- Silverlight
- Dart
註:Dart 專案原先的目的是開發一個取代 JavaScript 的新語言,在這個目的上,Dart 已經失敗了,但 Dart 仍有其他的用途,故 Dart 本身不算失敗的專案。
除非手上有一些專案要維護,否則不建議繼續學這些前端技術。行有餘力的話,最好把手上的舊程式以標準前端技術移植或改寫。
網頁前端技術的現在和未來
WebAssembly 是一個新興的網頁技術。透過 WebAssembly,我們可以用 C、C++、Rust 等語言撰寫網頁前端程式,效能會比用 JavaScript 來寫好得多。現代瀏覽器皆已支援 WebAssembly,所以 WebAssembly 是值得投資的技術。
由目前相關資料來看,WebAssembly 不會完全取代 JavaScript,而是以類似延伸模組的概念和 JavaScript 合作,所以 JavaScript 短期內不會被淘汰。本文不深入討論 WebAssembly。
運行在不同環境的 JavaScript
JavaScript 原先是專門運行在瀏覽器內的網頁內嵌語言,而非通用型語言。所以傳統上將 JavaScript 視為一種瀏覽器命令稿。目前 JavaScript 最主要的用途仍是用來寫網頁前端程式。
在 Node.js 這個獨立的 JavaScript 運行環境出現後,JavaScript 跨出原先的界限,在許多面向都可使用 JavaScript 來撰寫程式。
目前來說,常見以下應用:
- 網頁前端:JavaScript 原先的角色
- 網頁後端:用 Node.js 寫網頁程式,時常搭配 express.js 等網頁框架
- 桌面端:使用 Electron 等框架寫桌面軟體
- 行動端:使用 Cordova、React Native、NativeScript 等框架寫行動軟體
- 物聯網:像是 IoT.js
在學習 JavaScript 時,要注意當下的 JavaScript 運行環境是瀏覽器還是 Node.js。有很多 JavaScript 初心者拿 Node.js 來學 JavaScript,在語法上是相容的,但在套件格式及部分 API 則不相容,需注意。
即使撰寫網頁前端程式,仍然可搭配 Node.js 套件的開發工具,只要避開 API 不相容的地方即可。
JavaScript 轉譯器
許多對 JavaScript 的批評,都是來自於 JavaScript 本身語法上的缺陷 (可參考這裡)。為了在繼續使用 JavaScript 運行環境的前提下改善其缺失,出現許多 JavaScript 轉譯器 (JavaScript trans-compiler),一些例子如下:
- CoffeeScript
- TypeScript
- Babel (即 ECMAScript 6+)
- Elm
- Dart
- Transcrypt (Python 轉 JavaScript)
- Opal (Ruby 轉 JavaScript)
- GopherJS (golang 轉 JavaScript)
- ClojureScript (Clojure 轉 JavaScript)
讀者可能會覺得選擇過多,不知道怎麼選。以國外的流行度來說,目前 TypeScript 第一,而 Babel 是第二,其他的工具則使用者更少。當然,流行度只是一個參考,每個工具的特性不同,讀者可自己選擇。
如果還沒開始學前置語言的話,筆者建議從 TypeScript 或 Babel 開始學。因為這兩種前置語言大抵上相容於原生語言,可直接套用在現有的專案上,藉由較好的語法重構現有的程式,而不需重寫。
在網頁中使用 JavaScript
有兩種方式:
- 直接內嵌 (embedding) 在 HTML 文件中
- 引入外入 JavaScript 命令稿
第一種方法實例如下:
// Assume jQuery is loaded.
<script>
$("#target").html("Goodbye World");
</script>
此例會將 id #target
內的文字改為 Goodbye World
。
第二種方法如下:
<script src="js/app.js"></script>
此處使用相對頁面,會使用 js 目錄下的 app.js 命令稿。
也可以使用外界的命令稿:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
此處會從 cdnjs.cloudflare.com 站台引入 jquery.min.js 命令稿。
除非 JavaScript 程式碼量很短,最好將 JavaScript 的部分分開來,不僅易於維護,也可在重覆瀏覽時用快取 (cache) 節省網頁載入時間。
基礎 JavaScript 實例:切換按鈕
本節用一個簡短的例子來看如何在頁面中使用 JavaScript。我們把完整的程式碼放在這裡,本節僅節錄部分內容。
首先來看 HTML 的部分:
<div id="border">
<div id="target">Hello World</div>
<button id="btn">Toggle</button>
</div>
HTML 的部分相當簡單,只有一行文字和一個按鈕。CSS 僅影響版面美觀與否,對功能沒有影響,此處不列出。
以下是 JavaScript 的部分:
let clicked = false;
$("#btn").click(() => {
if (!clicked) {
$("#target").html("Goodbye World");
clicked = true;
} else {
$("#target").html("Hello World");
clicked = false;
}
});
這個是 ES6+ 搭配 jQuery 寫成的小程式,在 CodePen 中會透過 Babel 自動轉為等效的 ES5 程式碼。若讀者一開始無法完全看懂也無妨,先體會一下程式的感覺。
我們用 clicked
的狀態切換程式的行為,當 clicked
為 false
時,程式會將 #target
內的文字轉為 "Goodbye World"
,並將 clicked
切為 true
。第二次按時,程式會將 #target
內的文字轉為 "Hello World"
,並將 clicked
切回 false
。
我們將整個程式的行為寫在一個回呼函式 (callback) 內,綁定到 #btn
的 click 事件上。
對 JavaScript 做靜態程式碼檢查
由於 JavaScript 本身的語法特性在工程性上比較弱,搭配 linter 檢查 JavaScript 程式碼是一個常見的方式。本文使用 ESLint,主要的好處在於可檢查 ES6+ 代碼,也可和 Babel 搭配使用,透過以下指令安裝:
$ npm install --save-dev eslint babel-eslint
第一次使用前要先初始化:
$ node_modules/.bin/eslint --init
可參考以下 .eslintrc.js :
module.exports = {
"env": {
"browser": true,
"es6": true,
"jquery": true
},
"parser": "babel-eslint",
"plugins": [
"flowtype"
],
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2015
},
"rules": {
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"windows"
],
"quotes": [
"error",
"double"
],
"semi": [
"error",
"always"
]
}
};
由於我們將 JavaScript 用於前端,故採用這些設置;如果讀者將 JavaScript 用在其他環境,則需修改此設定檔。
透過修改 package.json 來串連指令:
{
"script": {
"jslint": "eslint src/js/*.js",
...
}
}
執行指令即可在專案中呼叫 ESLint:
$ npm run jslint
縮小 JavaScript 程式碼以節約頻寬
在 JavaScript 中,空白和換行只是讓程式碼看起來比較美觀,但對於執行 JavaScript 程式沒有實質的幫助。實際要上線的 JavaScript 程式碼會透過縮小 (minification) 移除多餘的空白和換行以減少程式碼所占空間,傳輸到客戶端時更快速。
縮小原生 JavaScript 代碼
此處的原生 JavaScript 是指 ES5 (含) 以前的 JavaScript。在本節中,我們使用 UglifyJS,透過以下指令即可安裝:
$ npm install -g uglify-js
透過以下指令即可縮小程式碼:
$ uglifyjs path/to/src/file.js -o path/to/dist/file.min.js
縮小 Babel (ES6+) 相容的 JavaScript 代碼
前一節使用的 JavaScript 縮小器適用於 ES5,對於 ES6+ 的程式碼可考慮 babel-minify,透過以下指令安裝:
$ npm install --save-dev babel-minify
修改 package.json 以串連指令:
{
"scripts": {
"jsminify": "minify src/js -d dist/js"
}
}
前述的使用方式是單獨使用該套件,也可以和 Babel 整合,執行以下指令以安裝相關套件:
$ npm install babel-preset-minify --save-dev
這時候要修改 .babelrc :
{
"presets": ["env"],
"env": {
"production": {
"presets": ["minify"]
}
}
}
切換 BABEL_ENV
時,Babel 會在轉譯 JavaScript 代碼時一併縮小代碼:
$ BABEL_ENV=production npm run build
繼續深入
由於 JavaScript 的生態系相當龐雜,很容易讓人產生資訊焦慮,筆者的建議是只要挑自己需要看的資料即可。一般來說,JavaScript 的教材 (書籍、網站、影音等) 約略分為以下數種:
- 語法:可能為 ES5 或 ES6+
- 函式庫:jQuery、Dojo、D3.js 等
- 框架:Angular、React、Vue 等
- 工具:Grunt、Gulp、Webpack 等
- 前置語言:TypeScript、CoffeeScript、Dart 等
- 其他領域:Electron、React Native、NativeScript 等
註:Babel 算前置語言,但其語法剛好是 ES6+,故歸類於語法相關內容。
即使到了 2018 年末,使用 jQuery 並不可恥,只要 jQuery 符合自己專案的需求即可。同樣地,如果專案用 Grunt 跑得好好的,沒事也不用特意改成 Webpack。
附註
我們在這裡對 JavaScript 的語法進行較詳細的講解,有興趣的讀者可以看一看。