網頁程式設計教學:JavaScript 入門

PUBLISHED ON NOV 27, 2018 — WEB

    如同先前簡介 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 來寫好得多;目前還在發展早期,網路上可以找到一些原型 (prototype)。由目前相關資料來看,WebAssembly 不會完全取代 JavaScript,而是以類似延伸模組的概念和 JavaScript 合作,所以 JavaScript 短期內不會被淘汰。本文不深入討論 WebAssembly。

    JavaScript 相關應用

    JavaScript 原先是運行在瀏覽器內的語言,在 Node.js 出現後,JavaScript 跨出原先的界限,在許多層面都可使用 JavaScript。目前來說,常見以下應用:

    有很多 JavaScript 初心者拿 Node 環境來學 JavaScript,在基本語法上是相容的,但在套件格式及一些 API 則不相容,需注意。即使撰寫網頁前端程式,仍然可搭配 Node 下的工具,只要避開不相容的地方即可。

    JavaScript 轉譯器

    許多對 JavaScript 的批評,都是來自於 JavaScript 本身語法上的缺陷 (可參考這裡)。為了在保持相容性的前提下改善其缺失,出現許多 JavaScript 轉譯器 (JavaScript trans-compiler),一些例子如下:

    讀者可能會覺得選擇過多,不知道怎麼選。以國外的流行度來說,目前 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 的狀態切換程式的行為,當 clickedfalse 時,程式會將 #target 內的文字轉為 "Goodbye World",並將 clicked 切為 true。第二次按時,程式會將 #target 內的文字轉為 "Hello World",並將 clicked 切回 false。我們將整個程式的行為寫在一個回呼函式 (callback) 內,綁定到 #btn 的 click 事件上。

    繼續深入

    由於 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 做靜態程式碼檢查

    由於 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 縮小器適用於 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