跳過導覽列

O3noBLOG

A Happy(?)Designer ~~

YUICompressor vs Closure-Compiler

總之我比較喜歡後者,兩個原因,一是壓縮出來比較小,二是不會把換行砍光光,這樣如果有錯誤出現,不得不從壓縮過的版本裡面找時,還會比較好找,用 YUICompressor 的話,因為會只剩一行,結果有錯誤的話也無法判斷是哪一部分的程式碼出錯。

JavaScript Best Practice Part.1: JSLint

前陣子有長輩問我 JavaScript 的 Best Practice,一時還真講不出來,因為我不太有把經驗整理出來的習慣,所以有了這系列的文章,雖然會有幾篇不知道XD。

那天被問到的時候我一時只想的到先過JSLint再說,所以第一篇就先從 JSLint 開始講起,JSLint 是Douglas Crockford在 2002 年時發表的 JavaScript 程式碼的檢查工具,除了基本的語法檢查外,還多了不少限制和要求,可以讓你的程式碼品質提昇,光是讓你的程式碼能過 JSLint 檢查就可以減少很多可能的問題了,接下來就針對各項主要的檢查項目做介紹。

閱讀 「JavaScript Best Practice Part.1: JSLint」 全文

jQuery Events

兩篇不錯的文章。

jQuery special events,這篇介紹了 jQuery 的 special event api ,讓你可以自定事件,文中的範例是 triple click (連點三次),文章有點長,講的蠻完整的,要實際應用比較有難度。

Demystifying custom events in jQuery,這篇則是講 custom event ,翻成中文都是自定事件,不過前一篇是定義出通用於所有元件的新事件,這篇講的則是一種不一樣的程式設計導向,更加的以事件為導向,例如在你的狀態顯示器上加上一個「切換狀態」的事件,訪客可能按了某個按鈕,那個按鈕的點擊事件會去觸發顯示器的切換狀態事件,因為 jQuery 的 event api 寫的夠好,所以可以很簡單的實作出來。

jslint 和 command line js

這邊是記錄用,編譯 spidermonkey 1.7 並支援 UTF-8 的方法:

curl -O ftp://ftp.mozilla.org/pub/mozilla.org/js/js-1.7.0.tar.gz
tar zxvf js-1.7.0.tar.gz
cd js/src
export CFLAGS="-DJS_C_STRINGS_ARE_UTF8"
make -f Makefile.ref
sudo JS_DIST=/usr make -f Makefile.ref export

來源:INSERT SOMETHING CREATIVE

另外 v8 要拿來跑 jslint 的話,console 要設成 readline ,ubuntu 的話編譯前要先裝libreadline-dev,加上 GCC 4.4 的話要不一樣的參數,編譯指令是:

GCC_VERSION="44" scons d8 console=readline

沒加上 console=readline 的話不管怎樣測試都會跟你說沒問題~

另外有一篇インストール - JavaScirpt (V8, TraceMonkey, SFX介紹了怎樣編譯 V8, TraceMonkey, SFX 的方法

jQuery() in 1.4

jQuery 1.4有個改變應該很少有人注意到,我也是最近剛好有需求才發現,就是直接執行 jQuery 不傳任何參數:

jQuery();

結果會傳回一個空的 jQuery set,不過這在 1.4 以前的版本會傳回document,這樣的修改我覺得是比較好的,因為以前完全沒辦法產生空的 jQuery set,如果要自己做 jQuery set 會比較方便,除了把要的 DOM 節點抓好放陣列丟給 jQuery 外還多了個建立空的 jQuery set 後一個一個把要的節點丟進去的方法,另一個優點是這樣確保 jQuery function 傳回來的物件是同樣的類型。

JavaScript 的一些效率問題

前幾天在測試each和 for 迴圈的效率時,意外的一直得到 each 效率比較好的奇怪現象,搞了兩天才找到原因。

each 這種方法效率會比 for 迴圈還要低主要是因為它是把要做的事情用 function 傳進去,多了一個 function call 和一層 function scope,要對變數作存取時會多了到不同層 scope 尋找的差,所以理論上它會比 for 迴圈還要慢。除此之外,DOM 本身就很慢了,當然DOM NodeList的操作和存取也不會快到哪去,所以像Sizzle引擎就會把 DOM NodeList 轉成陣列再傳回來,而我測試 each 和 for 兩者的效率時,也就是這個部分產生了非預想的結果,根據測試結果 Google V8 和 Firefox 3.6 的 Tracemonkey 這兩個會編譯 JS 的引擎,第二次對同樣內容的 DOM collection 轉陣列的動作時會比第一次還要快,而且大約是兩倍快,測試的基準是第一次用getElementsByTagName抓 <span> ,第二次用 jQuery,內部也是一樣用 getElementsByTagName ,並且小修改過 DOM 結構後再做一樣的事情也是會比第一次還快,並且,不管是用slice還是 for 迴圈一個元素一個元素推到陣列裡面都沒差很多,一開始因為測試都是對同樣的標籤作處理,結果先測的方法就佔了劣勢,就像是美食漫畫一樣,先上菜的都會輸一樣,不過我對編譯器的怎樣做最佳化的方法不熟,所以這兩個引擎是怎麼辦到的就不清楚了。

除了這點之外,還有一些不算小發現的小發現,第一個就是 Firefox 3.6 還是好慢,詳細數據我忘了,不過和 V8、Safari 比起來差距還是不小,然後就是 IE 超.級.慢!!第二是 each 真的比較慢,不過在現在 JS 引擎普遍暴力的情況下差距其實不明顯(不過 NodeList 和陣列的差距還是有)。第三是前面已經講過的 Sizzle 回傳的是陣列而不是 NodeList ,雖然實際上想要自己組合 NodeList 似乎也是不可能的。最後是 jQeury 可以用$("span")[0]這種寫法來直接存取 DOM 節點,不過它並不是陣列,要轉成純陣列可以用$("span").get(),只是測試過後發現沒有比較快,因為還要重新轉一次陣列,這裡損失的時間也不會比直接存取來的少。

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 || {};
})()

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);

結果如下:

OperaFirefoxIE
"blah"234313344
new String()156381266
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 的資料型態。

此類別所有文章