簡單的 JavaScript 自動單元測試

前幾天弄完 JSLint.vim 後,就對 server side JavaScript 產生了一些興趣,還抓了 v8 引擎 來用,於是就想到之前想弄很久的自動單元測試,想說要跑 QUnit ,不過 QUnit 雖然獨立於 jQuery 了,但還是要在瀏覽器環境下才能執行,純 JavaScript engine 還少了 DOM BOM,就是說它需要 document, window 這些物件,找了一下發現 John Resig 有弄 env.js 這個專案,就是要在 js shell 裡面做出瀏覽器的環境,不過問題是目前只能跑在 Rhino 下,因為他是 java based ,可以在 JavaScript 裡面寫 Java ,所以可以做很多壞事,但是其他的 js shell 沒辦法這樣跑, John Resig 好像有想要 port 成 python + v8 的版本,不過不知道有沒有成功,因為也 沒放出

閱讀「簡單的 JavaScript 自動單元測試」全文

closure-compiler on OSX

Google 的 Closure Cmpiler 要 Java 1.6 ,蘋果雖然有提供 1.6 的 更新 ,不過在終端機下直接打 java 還是 1.5 版,至於 1.6 版則是放在

/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Commands/java

如果要跑 closure-compiler 建立shell script 時 java 執行檔要改用這個。


var foo = foo || {};

foo = foo || {};

這樣的寫法大家一定不陌生,如果 ab 有值的話就繼續用,不然就讓他成為空物件。通常會用到的地方有兩種,函式設定參數預設值,或是跨檔案的公用函式庫,這種時候會把變數放在 global scope 下,也就是全域變數,要宣告全域變數的話不用 var,直接變數名稱就可以了,就像上面那段 code,不過實際上,這樣寫是會出錯的,沒寫 var 的話,第二個 foo 會跑出 foo 尚未定義的錯誤,所以寫成標題那樣是比較沒問題的,不過有時候,程式碼會全部包在一個 function scope 裡面,避免安全的問題,但是在這個 scope 裡面用 var 宣告變數的話,變數又不會是全域的,這時其實加上 window 就可以了:

(function () {
foo = window.foo || {};
})();

也可以用 if 判斷:

(function () {
if (! window.foo ) { window.foo = {} }
})();

最後我還發現一件趣事,大家可以猜猜看下面的 code 會不會有錯誤:

(function () {
foo = undefined;
foo = foo || {};
})()

spidermonkey 的 UTF-8 支援

JSLint 其實是用 javascript 寫的 javascript 語法檢查工具, jslint.vim 並沒有把它轉成 vim script ,而是呼叫 JS 引擎來跑 JSLint ,最好找的 JS 引擎就是 mozilla 的 spidermonkey ,很多 linux 有包好的套件,FreeBSD 也有 port ,不過實際上用的時候發現有個問題,就是不支援 UTF-8 字串,結果就是在 JSLint 的字元檢查時會把一些中文當成不安全字元,例如:「下」。

其實要 spidermonkey 支援 UTF-8 字串也不難,FreeBSD 的話在裝 port 時加個參數就可以了,OSX 的話把 MacPorts 更新到新版的再裝就可以了,Linux 的話就沒辦法用套件安裝,要自己抓來編, 1.7 的話要去修改 Makefile 讓 CFLAGS 加上 "-DJS_C_STRINGS_ARE_UTF8" 這個參數,或是參考 mongoDB 的作法,如果是 1.8 版的話有內建支援,不用修改 makefile,但是以後要執行的 js 檔案裡面都要先呼叫 JS_CStringsAreUTF8 這個函式,以 jslint.vim 來說就要加到 ~/.vim/plugin/jslint/runjslint.js 這個檔案。

其實要解決 UTF-8 問題還有其他方法,就是換其他的 JS 引擎,像是 Google V8 , 要用 V8 的話 編譯 時要編 developer shell ,產生的執行檔檔名叫 d8 ,弄個鏈結讓 js 這個指令可以執行就可以了,V8 直接支援 UTF-8 ,弄起來就可以用了,不用再去改其他東西,理論上速度應該也比較快吧,JSLint 都跑很快,無從比較XD。


JavaScript on vim

因為現在主要都是在寫 javascript 為主,所以這兩天調整 Vim 主要目標都是為了 javascript,這篇整理一下使用的 plugin 和相關設定,不過在開始前,請先把 Vim 升級到 7.2,套件沒有的話自己編譯也可以。

syntax/indent

Vim 雖然有內建支援 javascript 的縮排和語法標籤,不過另外都有人維護比較完整的版本,而且不只一種,我沒詳細比較,只是挑看起來比較有在維護的:

照說明把檔案放到正確的位置即可,另外雖然有 jQuery 的 syntax ,不過我安裝後發現會和 The NERD Commenter 衝突,而且看一下內容覺得也沒做的很好,就沒用了。縮排的部份也有人是用外部程式來處理,詳細可以 vim-taiwan 上的 討論 ,我目前是還沒覺得有需求。

閱讀「JavaScript on vim」全文

htmlcomplete#CompleteTags 的 bug

今天又在玩 vim 自動完成時發現的,網路上也有找到一點點情報,不過資料實在很少,我花了不少時間測試找出會發生問題的狀況,這個 bug 是在使用 autocomplpop 時,游標放到 class=" or id=" 這兩個字串後會有錯誤訊息 (line 304, E121: Undefined variable :classlines)。網路上找到有人 回報給 acp.vim 作者 ,日本那邊也有人 hack acp.vim 來避開這個問題 ,不過其實問題不在 autocomplpop,而是 vim 內的 htmlcomplete 的問題,這個 function 位置在 $VIMRUNTIME/autoload/htmlcomplete.vim ,上次更新是 2006 年了,之後回報 bug 會不會有人修還不知道XD。

閱讀「htmlcomplete#CompleteTags 的 bug」全文

Zen Coding on vim

前兩天試了一下,還蠻厲害的,不過不知道是不是我現在的設定有點亂,所以沒辦法 tab 觸發自動完成,一定要按 Ctrl + E,另外就是目前只支援 HTML 的樣子,我看原始碼發現判斷檔案格式的函式直接回傳 "HTML",不過這也還好,因為 CSS 有其他自動完成可以用,zen-coding 在 CSS 處理上沒特別強,但是 HTML 要到這樣厲害目前沒其他方案,下面記錄一下安裝方法。

先到 這串討論 抓最下面的 zencoding.vim 和 zencoding_vim.py 兩個檔案,放到 ~/.vim/plugin/ 裡,再來到 zen-coding 下載頁 抓 Zen Coding for TextMate v0.3.1.zip, TextMate.Zen.HTML.1.3.1.zip, TextMate.Zen.CSS.1.3.1.zip 三個檔案一樣解開放到 ~/.vim/plugin/ 裡面,這樣就可以了,測試方法就是隨便打個 css selector 然後按 Ctrl+E ,另外記得 selector 裡面不要有空白,這個 plugin 其實是用 Vim 跑 python ,所以 Vim 要先支援執行 Python Script ,我的就是裝完就可以,所以沒對這部份下去研究,如果有人跑不起來可以看看是不是這方面的問題。


String and String Object

JavaScript 中,基本的資料型態有數值、字串、布林三種,而這些資料的指派都有兩種方法,一種是直接指派值,另一種是用建構函式,以字串為例:

var sv = "1234567890";
var so = new String("1234567890");

以前我一直以為兩種是等價的,不過在看 Efficient JavaScript 這篇文章時才發現兩者不是完全相等,在使用字串的 method 如 .charAt() 時,第一種字串值要先轉換成第二字串物件,然後才執行 .charAt() ,當需要大量執行字串的 method 時,用第二個方法來宣告字串變數理論上會比較快,不過我實際測試之後才注意到文章該段最後有說到這是 Opera 才有特別做最佳化的,測試用的code如下:

var s = "1234567890";
//var s = new String("1234567890");

var start = (new Date()).getTime();
for( var j = 0; j < 10000; j++ ) {
  for( var i = 0; i < s.length; i++ ) {
    s.charAt(i);
  }
}
var end = (new Date()).getTime();
alert(end-start);

結果如下:

Opera Firefox IE
"blah" 234 313 344
new String() 156 381 266
unit: micro seconds

可以發現 Opera 用 new String() 時有比較快, IE 雖然也有,不過我在某些電腦上測試結果是和 Firefox 差不多,沒列出來的 safari 其實表現也和 Firefox 差不多,至於 Google Chrome 在編譯的時候有最佳化過,結果都是超快,無法作為參考。所以結論是,還是繼續用普通的方法就好了XD。


JavaScript 二三事

好久沒寫 blog 了,來記錄一下最近和 JavaScript 有關的消息,首先是新書, High Performance JavaScript ,是除了 JavaScript Ninja 外我最近比較期待的新書,不過兩本都是明年才會出,JavaScript Ninja 是 John Resig 寫的,目標讀者是開發 JavaScript Framework 的開發者,所以內容相當深,我有先買預覽的電子版,裡面的 code 就常常看到暈頭, High Performance 那本則是 YUI 的開發者之一 Zakas Nicholas 寫的,之前也寫過不少書,不過我都沒看過就是,目前期待主要是書名的關係。

第二個則是 Loading JavaScript as strings 這篇文章,提到如果先把 js 程式碼先用字串形式讀進來,再用 eval() 來執行速度反而會比直接邊讀邊執行還要快,這項技巧可以用在 js 量大的 web application,對於之後才要用的 code 就用這種方法讀,讓頁面的生成可以快一點,詳細的內容在 12/8 的 O'Reilly Velocity Online Conference 會介紹並 demo ,換算成台灣時間好像是週三凌晨一點多開始。

最後一個不是新東西,其實是 Google 的文章 ,裡面有講到會造成 memory leak 的原因,第一種是 IE 的 event function 可能會造成記憶體浪費(其實原因和下面的很像),解法是自己寫一個 event dispatcher ,像是 jQuery 就是這樣,第二種就是把資料附加在 DOM 元件上,例如:

var node = document.getElementById('getMe');
node.myData = 123;

像這樣可以把資料付在 DOM node 物件上,再某些情況下很有用,但是如果這些附加的資料包含其他 DOM node 的話,就會造成 memory leak ,建議的處理方法是完全不用這種特性,至於 jQuery 則是有個 data 可以用,用法就像是 key, value 的資料型態。


更之前的文章