2008-05-04 23:16

好用的 lex 詞彙解析語言

lex 這個語言是我在二技時學會的,這原本是用來開發編譯器的語言,但他對於詞彙解析有很強的能力,而且不需要寫太多的程式碼就可以做到很大的能力,用來處理一些有規則文檔的是輕而一舉的事。

其實 lex 的語法就是『正規表示式』所組成的,所以撰寫上花的腦筋跟測試會比寫程式碼來的多,雖然很好用,但在想解析的規則時卻不是那麼快樂。

lex 是 base 在 C 上面的語言,除了解析的規則外,其他都是屬於 C 語言的撰寫,所以基本上還必須會寫 C,而且 C 的文字處理函數卻不怎麼好用。


lex&yacc
以入門書來說這本寫的真是不錯,除了語法上都講解得很清楚,還有有很多教學範例。
相關連結:Yacc 与 Lex 快速入门
2008-05-04 13:55

在 IE 中對表格的操作問題(innerHTML,style)

問題1:在對以存在的表格用 innerHTML 新增列或其他 DOM 子節點時,會發生錯誤或警告,或者是完全沒反應。
  1. table.innerHTML+="<tr><td>test</td></tr>"; 


解決方法:採用 createElement 去建立 DOM 物件,再用 insertBefore 插入新的子節點。
  1. var tdObj=document.createElement('td'); 
  2. trObj.appendChild(document.createTextNode("test")); 
  3.  
  4. var trObj=document.createElement('tr'); 
  5. trObj.insertBefore(tdObj); 
  6.  
  7. table.insertBefore(trObj); 


問題2:在現存表格中的子節點(tr,td)作 style 操作時,完全沒反應的現象。
  1. tr.style.display="none"; 
  2. td.style.display="none"; 


解決方法:利用 className 去套用以設定好的樣式。
  1. table .hide{ display : none; } 
  1. tr.className="hide"; 
  2. td.className="hide"; 


目前我只在 IE 上遇到這些問題,Firefox 則不會有這樣的問題,可能是 IE 對 table 的 DOM 解析比較特別,其他的 DOM 物件都不會有類似的狀況。
2008-05-03 22:00

JavaScript 效率優化

最近開始重視 JavaScript 的效率問題,原因在於感到自己寫的網頁越來越不流暢,其中大量的計算跟無法避免的迴圈愈來愈多,加上 UI 介面跟動態效果的需要,使得瀏覽器的負荷過大,所以這是個需要重視的問題,這直接的影響網站名聲的好壞。

要如何作效率優化呢?因為 JavaScript 是直譯語言,無法透過編譯器加以優化,所以只能從撰寫方式下手。

下面有一個效率很差的例子:
  1. for (var i=0; i<data.length; i++) { 
  2.    document.getElementById("link").innerHTML += data[i]; 
  3.    document.getElementById("link").hreh += "&" + data[i]; 
  4.    document.getElementById("link").title += data[i] + ","; 
  5.    document.getElementById("link").className += data[i] + " "; 
  6.    document.getElementById("link").name += data[i] + "_"; 
  7. } 


當中有幾個影響效率的問題:
  1. 過渡的使用 getElementById 搜尋 DOM 物件,尤其是在 for 迴圈中
  2. 大量的存取 length、innerHTML 等成員變數
  3. 多次的存取 data[i],這跟上一個差不多



這些問題的修改方法:
  1. /*利用暫存變數作處理*/ 
  2. var temp1='', temp2='', temp3='', temp4='', temp5=''; 
  3.  
  4. /*利用 l 紀錄 length 的值,以避免多次存取成員變數*/ 
  5. for (var i=0,l=data.length; i<l; i++) { 
  6.    /*利用 d 紀錄 data[i] 的值,以避免多次存取成員變數*/ 
  7.    d = data[i]; 
  8.    temp1 += d; 
  9.    temp2 += "&" + d; 
  10.    temp3 += d + ","; 
  11.    temp4 += d + " "; 
  12.    temp5 += d + "_"; 
  13. } 
  14.  
  15. /*利用暫存變數紀錄 DOM 物件*/ 
  16. var link = document.getElementById("link"); 
  17. link.innerHTML += temp1; 
  18. link.hreh += temp2; 
  19. link.title += temp3; 
  20. link.className += temp4; 
  21. link.name += temp5; 



實驗 1000 筆資料的結果:
第 1 個耗時:5625(msec)
第 2 個耗時:47(msec)
展示(demo)

參考來源:
JavaScript loop performance
随時innerHTMLに書き込んだ場合と一括して書き込んだ場合
innerHTMLとinnerText/textContentの速度比較
getElementByID、変数、withによる参照速度比較
シリアルサーチと正規表現の速度比較
2008-05-03 16:01

善用 Mootools 的 onComplete 事件

用了 Mootools 那麼久終於有點心得了,尤其是在 Effects 類別庫上,因為之前一直對變換效果的後續處理很困擾,而採用的延遲函數,可是這不是很好的作法,因為 JavaScript 的計時器非常的耗資源,會讓整個網頁的流暢度降低。

而其實在 Effects 都附有 onComplete 事件可以使用,這個事件會在變換效果結束後被呼叫,而且還附帶本身物件(this),利用 firebug 的 console.log() 函數可以直接觀察 this 所帶有的成員參數,這比之前的延遲函數要好多了,而且程式碼也不會那麼多。

其他的類別庫中也有定義或繼承 onComplete 事件,如 Drag、Remote 及 Plugins 中的部分類別都帶有此事件,雖然官方的 docs 沒有寫得很清楚,只寫了繼承的來源及本身帶有的成員函數,有部分的成員變數可能要看原始碼會比較清楚,Mootools 的設計架構其實在撰寫上還蠻具彈性的。
2008-05-02 22:56

用 JavaScript 作 HTML 比較(diff)

為了做到文字比較,我利用『最長共同部分序列』(Longest Common Subsequence)演算法,這是屬於動態規劃演算法的一種,特點就是以空間換速度,在比較過程需要 (m+1)*(n+1) 的記憶體空間,效率是 m*n,m 跟 n 的數量取決於文章的長度與切割的大小,我的切割方式是以空白符號及 HTML tag 作分界,對英文的切割有正向的優勢,所以在中文上就有點糟,但因為切割的長度大效率會比較快。
  1. /*切割文字*/ 
  2. function toReg(str){ 
  3.    var array = str.match(/<[^>]*>|[^< ,.\r\n\t]+[ ,.\r\n\t]*/ig); 
  4.    result = []; 
  5.    for(var i = 0, l = array.length; i < l; i++) { 
  6.        if(array[i].charAt(0) == '<') { 
  7.            temp = array[i].match(/[^<][^> ]*/i); 
  8.            result[i] = { 
  9.                txt: array[i], 
  10.                tag: temp 
  11.            }; 
  12.        }else{ 
  13.            result[i] = { 
  14.                txt: array[i], 
  15.                tag: false 
  16.            }; 
  17.        } 
  18.    } 
  19.    return result; 
  20. } 
  21.  
  22. /*最長共同部分序列*/ 
  23. function LCS(na, oa){ 
  24.    var m = na.length, n = oa.length; 
  25.    var i, j; 
  26.    var d = []; 
  27.  
  28.    /*Dynamic*/ 
  29.    for(i = 0; i <= m; i++) { 
  30.        d[i] = []; 
  31.        d[i][0] = 0; 
  32.    } 
  33.    for(j = 1; j <= n; j++) { 
  34.        d[0][j] = 0; 
  35.    } 
  36.  
  37.    /*動態規劃演算法*/ 
  38.    for(i = 1; i <= m; i++) { 
  39.        for(j = 1; j <= n; j++) { 
  40.            if(na[i - 1].txt == oa[j - 1].txt) { 
  41.                d[i][j] = d[i - 1][j - 1] + 1; 
  42.            }else if(na[i - 1].tag && na[i - 1].tag==oa[j - 1].tag) { 
  43.                d[i][j] = d[i - 1][j - 1] + 1; 
  44.            }else if (d[i][j - 1] > d[i - 1][j]) { 
  45.                d[i][j] = d[i][j - 1]; 
  46.            }else { 
  47.                d[i][j] = d[i - 1][j]; 
  48.            } 
  49.        } 
  50.    } 
  51.  
  52.    /*標註共同部分序列*/ 
  53.    i = m; 
  54.    j = n; 
  55.    while (i > 0 && j > 0) { 
  56.        if(d[i][j] == d[i - 1][j]) { 
  57.            i--; 
  58.        }else if(d[i][j] == d[i][j - 1]) { 
  59.            j--; 
  60.        }else{ 
  61.            i--; 
  62.            j--; 
  63.            na[i].com = j; 
  64.            oa[j].com = i; 
  65.        } 
  66.    } 
  67.    delete d; 
  68. } 
  69.  
  70. /*合併比較陣列*/ 
  71. function merge(na, oa){ 
  72.    var m = na.length, n = oa.length; 
  73.    var result = []; 
  74.    if(!m && !n) { return null; } 
  75.  
  76.    var i; 
  77.    var oldPrint = 0; 
  78.    for(i = 0; i < m; i++) { 
  79.        /*有共同的資料*/ 
  80.        if(na[i].com != undefined) { 
  81.            /*有刪除的舊資料*/ 
  82.            if(na[i].com > oldPrint) { 
  83.                var maxRow=(na[i].com < n) ? na[i].com : n; 
  84.                for(j = oldPrint; j < maxRow; j++) { 
  85.                    if(oa[j].tag) { 
  86.                        result.push(oa[j].txt); 
  87.                    }else{ 
  88.                        result.push('<del>' + oa[j].txt + '</del>'); 
  89.                    } 
  90.                } 
  91.            } 
  92.            /*記錄下一次舊資料的指標*/ 
  93.            oldPrint = na[i].com + 1; 
  94.            /*儲存共同的資料*/ 
  95.            result.push(na[i].txt); 
  96.  
  97.        /*新的差異資料*/ 
  98.        }else{ 
  99.            if(na[i].tag) { 
  100.                result.push(na[i].txt); 
  101.            }else{ 
  102.                result.push('<ins>' + na[i].txt + '</ins>'); 
  103.            } 
  104.        } 
  105.    } 
  106.    return result; 
  107. } 

展示(demo)
2008-05-01 22:14

在 IE 中對 window.open (子視窗)的參數傳遞

由於子視窗和母視窗是使用不同的 document,在 IE 的架構上不可以對母視窗作 call by reference,要先將要用的值設定給母視窗的變數,再作函數呼叫,在 JavaScript 中字串的屬性是一般變數值,不會有 reference 的現象。

  1. //要回傳的陣列 
  2. var selData=["A","B","C","D"]; 
  3.  
  4. //清空母視窗的陣列,將陣列長度設為零 
  5. window.opener.resultArray.length = 0; 
  6.  
  7. //將參數值填入母視窗的陣列中 
  8. for(i=0,l=selData.length; i<l; i++){ 
  9.    window.opener.resultArray.push(selData[i]); 
  10. } 


參考來源:請問子母視窗如何傳遞變數


結論:IE 真的很麻煩!難道不可以允許符合邏輯的語法嗎?