Build v8

平常是使用 Google V8 引擎的 command line 來做自己工作機的 js console,最主要的用途是跑 jslint,不過這需要自己來編譯,以前是用 scons 來編譯,剛剛想要編譯新版發現 Google 又換了自動化工具,從scons換成gyp,安裝流程其實比較簡單,先安裝好 svn,然後執行:

make dependencies
make console=readline native

產生的 d8 執行檔會在 out/native/ 下面,官方說明有提到可以用 clang 編譯,不過我嘗試後會有錯誤,還在看要怎樣處理。

武陵櫻花季

由於今年花況似乎很棒,所以很臨時的被耿長輩約去看,考量種種情形之後,決定週六晚上十二點就出發,一路上還算順暢,不過快到的時候就塞起來了,慢慢前進了快要一小時才知道原因,是買票入場的關口,等輪到我們時往後看是這樣的光景。

武陵農場

山路上全部都是車,進去以後首先就是找停車位了,想當然正常的停車格早就沒了,不過以往都會開放停草地,所以基本上就是找不太會影響交通的路邊停下去,結果沒停在太前面入口處,不過倒是找到一個位子意外還不錯的點,停好車後大約已經四點半了吧,由於有段櫻花道有打光的夜櫻可以看,所以先過去那邊看看,沒想到才看一下子就下起雨,所以也沒拍到什麼好照片,就回去車上等天亮了,沒想到這雨一下下整晚,我們就只好邊睡邊等雨停,大約等到九點雨好像停了才醒來開始移動,這時運氣不錯,太陽公公還出來露臉幾下。

武陵櫻花

武陵櫻花

看了一下地圖後,我們決定先過橋到離我們最近的茶園看看,我說的停車位子不錯的原因之一就是離茶園很近,那邊由於要走路過橋,所以完全不會有車子礙眼,個人覺的是武陵農場前半段最漂亮的地方了。

武陵櫻花武陵櫻花武陵櫻花武陵櫻花

在茶園那邊就逛了一個多小時,接著去到入口那附近,那邊基本上就是人人人,車車車,櫻花的狀況也沒有茶園好,不過還是拍到一張不錯的照片,剛好有陽光出來一下子,所以櫻花看起來都在發光一樣,邊邊都有一圈白框。

武陵

當然人多的話,就會看到一些有趣的場景,像是有人來拍婚紗:

武陵

還有人用 iPad 拍照:

武陵櫻花

不過總之前半段其實沒中後段漂亮,所以也沒待很久,就往最後一個目標,雪山登山口移動了,不過沒想到路上還有兩處櫻花狀況不錯,而且花況比前面看到的還好,當然也有不少情侶~~

武陵櫻花

武陵櫻花

最後開車到了雪山登山口,路上的景色蠻漂亮的,不過最後那段路的路況超差的,車子開起來真是心驚:

雪山登山口

雪山登山口

雪山登山口

這次行程可惜處之一當然是天氣,不過有些畫面也是要下完雨,放晴那時才看的到,一直都是藍天白雲的天氣也不一定就是好,第二當然是花況已經比較差了,要找到完整的櫻花其實不太容易,不過也托福可以看到櫻吹雪,還有一些滿地花瓣的畫面可以看,明年可能會開始總量管制,變成自己的小汽車不能開上去,這樣要上雪山登山口就會很困難,也很慶幸這次有上去看看,雖然天氣不好沒有去看日出就是(半夜開車上去應該超恐怖)。

jQuery.queue

jQueryqueue是存在很久的東西了,不過我到最近才去了解它到底是用來作什麼的,其實,基本上就和一般的 queue 一樣,只是 jQuery 預設使用它來處理所有的動畫效果的順序,像是 fadeIn、fadeOut 還有最重要的animate,這些特效的動作都會丟到一個叫 fx 的 queue 裡面,然後照順序執行,所以下面的程式碼會讓一個元素先 fade in 再 fade out,而不會發生兩個動畫效果打架的情形:

$('#id').fadeIn().fadeOut();

也不用寫成 callback function 的方式:

$('#id').fadeIn(function () {
    $(this).fadeOut();
});

如果你是要把元素 fade out,然後改裡面文字後再讓它 fade in,以前用 callback 的寫法會寫成:

$('#id').fadeOut(function () {
    $(this).html('new content');
    $(this).fadeOut();
});

但是好一點的作法應該善用 queue:

$('#id')
    .fadeOut()
    .queue(function () {
        $(this).html('new content');
        $(this).dequeue();
    })
    .fadeIn();

這樣不知道看不看的出好在哪?讓我來把要做的動作複雜一點好了,我要淡出,改內容,淡入,然後接著右移 500px、加上 active 的 class,再移動回來,然後再把內容改變一次,用 callback 的寫法:

$('#id').fadeOut(function () {
    $(this).html('new content');
    $(this).fadeIn(function () {
        $(this).animate({left: '+=500'}, function () {
            $(this).addCalss('active');
            $(this).animate({left: '-=500'}, function () {
                $(this).html('even new content');
            });
        });
    });
});

如果用 queue:

$('#id')
    .fadeOut()
    .queue(function () {
        $(this).html('new content');
        $(this).dequeue();
    })
    .fadeIn()
    .animate({left: '+=500'})
    .queue(function () {
        $(this).addCalss('active');
        $(this).dequeue();
    })
    .animate({left: '-=500'})
    .queue(function () {
        $(this).html('even new content');
        $(this).dequeue();
    });

眼尖的人大概馬上就可以發現,用 queue 程式碼變多了!不過這其實不是重點,重點其實是縮排的階層減少了,而且使用 queue 的程式碼,看起來就是一個步驟接著一個步驟,和使用 callback 的階層的關係不一樣,而且除了看起來比較好之外其實還有不少優點,首先是如果要修改連續動作的順序或是內容,使用 queue 的顯然比較簡單,二是還可以配合delay來讓動作之間有個暫停,三是可以用 clearQueue 來把整串動作清掉,用 callback 的話,因為執行到各個 animate 的時間不是同步的,所以用clearQueue可能會清不乾淨。所以在使用 jQuery 動畫效果時,正確的配合 queue 來做,會讓程式碼的可維護度提昇許多,大家都應該來用一下~

coffee-check.vim

上一篇文章提到使用RequireJSCoffeeScriptplugin時,如果寫的 CoffeeScript 語法不正確,瀏覽器即時 compile 就會有錯誤,而且在瀏覽器那邊看到的錯誤訊息根本看不出來是錯誤是在哪個檔案、哪一行,如果專案內檔案很多,那除錯會變得很難,所以我想到的解法就是在寫 coffee 檔的時候就檢查 compile error,就像jslint.vim的行為一樣,上網搜尋了一下Vim的 CoffeeScript 相關外掛,發現都沒人做這功能,於是花了一些時間從 jslint.vim 那邊移植過來,使用的檢查方式是直接coffee -c來編譯,看會不會有錯誤訊息,現在已經發佈到 github 上了,叫coffee-check.vim

安裝還算簡單,先確定系統已經有安裝 node,然後只要用pathogen或是Vundle把套件裝起來,接著在 vimrc 內加上一行:

au BufRead,BufNewFile *.coffee set ft=coffee

之後每次儲存 *.coffee 檔就會自動跑 CoffeeCompile compile,然後把錯誤訊息丟到 Quickfix Window 裡面,目前有一些已知問題:

  1. CoffeeScript compile 一次只會有一個錯誤,而不是從頭到尾的完整檢查,所以如果檔案內有兩個以上的錯誤,那就要重複幾次才能把錯誤都修正。
  2. 有一些錯誤訊息是沒有行號的,但是不給行號的話 Quickfix 那邊會有些行為不一樣,所以這種錯誤訊息我都統一說錯誤是在第一行。
  3. 第三是現在是用 node require 的方式來跑 compile,和系統自己用 npm 裝的 coffee 沒有關係,我還在考慮要不要用 npm 裝的 coffee 指令,一開始不用是因為用 coffee 指令是因為它 compile 出錯的話不只會有錯誤訊息,還會有錯誤的 trace 的資訊,不過這是我不需要的資訊。

第一和第二個問題基本上是目前無解,除非以後有什麼其它的 syntax error checker 來做這件事,現在的我也還寫不出這種東西,第三點則是近期可能會有更新,如果其它還有什麼建議或問題回報都歡迎,可以直接到 github 開issue

補充,有人提了我才發現,vim-coffee-script也可以做到幾乎一樣的事情,只要在 vimrc 加入這行:

au BufWritePost *.coffee silent CoffeeMake! -b | cwindow | redraw!

使用 vim-coffee-script 的話,因為CoffeeMake是把 coffee 轉成 js 的指令,所以如果沒有錯誤,就會產生 js 檔案,不過我自己的使用情境是不需要那個 js 檔案的,這也是我當初決定自己做一個的原因,單純只有做語法檢查。

Module, AMD, RequireJS

JavaScript 的物件並沒有封裝的概念,所有綁在該物件上的屬性都是外部可見的,不過還是有辦法做到物件封裝的效果,那就是Module Pattern,作法很簡單,想要保持 private 的私有屬性就宣告成建構函式的區域變數,在建構函式的最後回傳一組你要保持 public 的屬性或是 method,範例如下:

function Person(age, gender) {
    var _age = age || 16,
        _gender = gender || 1;

    return {
        getAge: function () {
            return _age;
        }
    };
}

這個範例中,age 和 gender 就是私有屬性,在 Person 物件外的操作無法碰到它們,唯一可以做的事情就是用 getAge 來讀取 age 的值,就這樣,我們有了有封裝特性的模組,不過在實際的應用的時候,還有一些問題需要處理,首當其衝的,便是模組之間的相依問題,不管你的程式架構多好,使用了各種設計模式來減少相依性問題,一定還是會有相依性的問題存在,在 server side 的 JavaScript 應用中,模組的相依問題還不明顯,因為你需要的模組都應該在本機系統存在,你的程式才能執行,以現在最熱門的nodejs來說,用的是CommonJS定的Module/1.0,只要把要匯出給其它人用的介面指派給 exports 這個變數,其它程式就可以很簡單的用 require 來取得,程式運作的流程就是很線性的從第一行跑到最後一行下來。

在網頁上的應用多了一個可能的變化,就是為了效能考量,讓一些資源像是 CSS、JavaScript 等檔案用非同步的方法讀取,這時候如果程式需要的模組檔案還沒讀下來,就還會有非同步執行的問題要處理,雖然 XMLHttpRequest 可以使用同步執行的方式,但是這樣會把整個瀏覽器定住,使用者用起來會覺得瀏覽器死當完全沒反應,所以這種作法完全不列入選擇之中,也因此有了AMD這個非同步模組的規範,它的寫法也很簡單:

define('moduleA', [dep1, dep2], function (export1, export2) {
    //do something..
    return {
        method1: function () {},
        method2: function () {},
    };
});

define 是一個全域的函式,專門用來宣告和註冊模組,它吃三個參數,前兩個都是非必要的,第一個參數是模組的名稱,其它模組如果有需要用到這個模組的話,就會用這麼名字來相認,第二個參數是一個字串的陣列,內容是需要的其它的模組名稱,第三個參數則是模組的建構函式,建構函數的參數則是根據前面第二個參數設定的相依模組來決定,會照定義順序傳入相依模組輸出的介面,而模型的建構函式最後面還需要回傳一個給其它人使用的介面,和 Module Pattern 一樣,其實背後的設計就是把建構函式當成相依模組建置完成的 callback function,所以就可以確保相依模組可以非同步的動態讀入,都準備好了才進入下一步。

AMD 最有名的實作就是RequireJS了,它完整實作了規範沒有講到的,瀏覽器動態且平行的讀取遠端主機上的檔案,用正確的順序執行,然後還把每個模組輸出的介面都管的好好的,不過其實 RequireJS 還提供了更多功能,像是作為 reousrce loader 來讀取 JavaScript 以外的資源,配合plugin可以直接寫CoffeeScript不需要先 compile 好,還有optimizeuglify等 deploy 相關的機制,讓開發環境和正式環境的接軌變得容易許多,不過上面講的都是優點,其實 RequireJS 還是有些缺點的,其中最大的問題就是文件條理不好,會讓把環境和設定搞起來這件事難度增加許多,再來是除錯變得困難許多,像是用 CoffeeScript plugin 即時編譯的話,剛好其中一隻 coffee 檔有語法錯誤,那會變的很難除錯等等,至於是利多還是弊多我覺得是利多,大部分的問題都是搞清楚就好了,CoffeeScript 的錯誤也可以用編輯器外掛來找。所以最後結論,我是蠻推薦可以導入 AMD 到中大型的 Web Application 專案的。

➡ 看看其它文章