JavaScript Advanced Skill

2,525 views

Published on

Introduce some advanced skills of JavaScript

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,525
On SlideShare
0
From Embeds
0
Number of Embeds
11
Actions
Shares
0
Downloads
25
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

JavaScript Advanced Skill

  1. 1. JavaScript Advanced Skill by David Hung 2010-11-22
  2. 2. JavaScript 的組成 一般來說, JavaScript 其實是由三個部份所組成 : ECMAScript 定義核心的程式語言架構、變數型態等 DOM (Document Object Model) 負責與頁面元素 (element) 溝通,例如修改元素的 HTML 內容 BOM (Browser Object Model) 負責與 Browser 溝通,例如取得螢幕的寬高等 JavaScript ECMAScript BOMDOM
  3. 3. 觀念澄清 在 JavaScript 裡可以 multi-thread 執行程式 ? 不可以 ! JavaScript 是一個 single thread 的語言,有一些 JavaScript Library 宣稱他們可以使程式碼跑起來像是 multi- thread 。其實是用 setTimeout function ” ”來 模擬 multi-thread 的 行為而已,但實際上還是 single thread 。 ※ 在 Browser 裡有一個 Queue 用來處理所有要執行的程式區 塊,利用 setTimeout 可以延後一個程式區塊的執行時間點。而 讓 Browser 有時間先做其他的事。 在 JavaScript 裡傳遞參數時,到底是 pass by value 還是 pass by reference? 全部都是 pass by value 。不過如果 pass 的參數型態為 reference type(i.e. object, array...etc.) 時,修改該物件裡的屬性 值,仍會影響到原先的物件資料。
  4. 4. DOM Event Flow 在 JavaScript 裡, Event 的傳遞分為兩個階段 : Capture phase target.addEventListener(type, listener, true); Bubbling phase target.addEventListener(type, listener, false);
  5. 5. 寫 JavaScript 的好習慣 (Good Practice) 一個 function 要碼都有 return 值 ( 在不同 condition 時 ) ,要 碼就都沒有。不要有時會 return 值,有時卻不會。容易造成 混洧。 function getShortBrowserName(obj) { if( obj.title == 'Firefox' ) { return 'FF'; } else if ( obj.title == 'Internet Explorer' ) { return 'IE'; } } 上面這段 code 不好的地方在於,如果傳入 object 的 title 屬性不為 Firefox 或 Internet Explorer 字串時,就不會 return 任何值。 Call 這個 method 的人就會接到 undefined , 而這可能不是預期中的事。
  6. 6. 寫 JavaScript 的好習慣 (Good Practice) 不要用 with statement 。因為它會造成執行效能降低,而且會 增加 debug 時的困難。 function updateBody() { with(document.body) { alert(tagName); innerHTML = 'Hello World'; } } with statement 是用來縮減 code 的長度,可省略在 statement 裡面 aceess 宣告的 object 屬性值的前置字串。但使用 with statement 時,在執行 code 會多找一層 scope 導致效能變差。改用比較短的 local varibable 先將該 object 記起來是比較好的寫法,如 下 : function updateBody() { var body = document.body alert(body.tagName); body.innerHTML = 'Hello World'; }
  7. 7. 寫 JavaScript 的好習慣 (Good Practice) if – else 裡的 statement 最好都要用括號括起來,即使只有一 行。這樣可以增加程式碼的可讀性。 每行 statement 最好都要有分號 (;) 結尾。雖然 JavaScript 允 許每行的結尾可以沒有分號,但這樣之後若要做 code 壓縮 ( 自動刪除空白和斷行 ) 時,就會產生錯誤了。 永遠不要用浮點數 (floating number) 來做 compare 比較。因 為浮點數在 JavaScript 裡是有誤差值的。 若要將一個字串 (string) 變數轉為數字 (number) 型態時,最 好是用 parseInt 或 parseFloat 作轉換。若是其他資料型態 (data type) ,則使用 Number() casting function 。
  8. 8. 寫 JavaScript 的好習慣 (Good Practice) 透過在 function 裡的 arguments 物件,可以模擬類似其他程 式語言 ( 如 : Java) 的多形 (Polymorphism)function 。在處理 時,可以根據傳入不同的參數型態或數量做不同的處理變 化。 // 加總函式 function sum() { var total = 0; for( var i=0; i<arguments.length; i++ ) { total += arguments[i]; } return total; } // 測試 code log( sum() ); // 0 log( sum(1, 2) ); // 3 log( sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) ); // 55
  9. 9. 寫 JavaScript 的好習慣 (Good Practice) 宣告一個變數時記得前面一定要加 var 。雖然不加 var 也是 JavaScript 可接受的寫法 ( 會變成 global variable) ,但是這樣 容易造成混洧和發生錯誤。 儘量避免明示宣告一個 primitive wrapped object(String, Boolean, Number ,例 : var str = new String(‘test…’);) 。因為 這會讓開發者搞不清楚他們是在處理 reference value or primitive value ,容易造成混洧。
  10. 10. 寫 JavaScript 的好習慣 (Good Practice) 在一個遞迴 (recursive) 或匿名 (anonymous) 函式裡,最好使 用 arguments.callee 代替指定 function name 。這樣一來,日 後即使換了 function name(reference) ,也不用跟著調整 function code 裡面寫到的名稱。 // a recursive function function grabData() { if( storage.length > 0 ) { // storage is a array var obj = storage.shift(); // your handle code here var method = arguments.callee; window.setTimeout(function() { method(); // execute grabData method actually }, 300); } }
  11. 11. 寫 JavaScript 的好習慣 (Good Practice) 在使用 Closure (a anonymous function inside another function) 時,只有在真正需要用到時才用這個寫法,別過度 使用它。因為它會保留上層 function 的整個 scope ,造成額 外的記憶體消耗。 儘用少用 setInterval ,因為它有可能會讓一段 code 還沒結束 前就開始執行下一次的程序。可以改用 setTimeout 加上遞迴 (recursive) 寫法來達成與 setInterval 一樣的效果,這樣可保 證每次執行 code 時,上一次程序已經執行結束。 如果要用 JavaScript 判斷不同瀏覽器或 Capability 來做例外處 理,最好當成是一般解法無法運作時的最後手段。非不得已 才用這個方式。
  12. 12. 寫 JavaScript 的好習慣 (Good Practice) 不要過度頻繁的操作 element 的 innerText 或 innerHTML , 因為這會降低執行速度,比較沒效率。如果有個迴圈會連續 對某個 element 改寫這兩個 properties 值,最好先將 HTML 或文字寫在 string buffer 裡,然後只做一次改寫的動作。 // 原本的寫法,比較沒效率,做了 100 次 DOM Element 操作 var panel = document.getElementById('my_panel'); for( var i=0; i<100; i++ ) { var item = document.createElement('div'); item.innerHTML = 'Item-'+i; panel.appendChild(item); } // 改用 String Buffer 的寫法,只做一次 DOM Element 操作 var panel = document.getElementById('my_panel'); var html = ''; for( var i=0; i<100; i++ ) { html += '<div>Item-'+i+'</div>'; } panel.innerHTML = html;
  13. 13. 寫 JavaScript 的好習慣 (Good Practice) 當使用指定 innerHTML 來改變一個 element 時,寫 code 時 最好不要將其 element 的 children 綁定 event handler 。如果 element 的 children 已經有綁定 event handler ,最好手動將 綁定的 event handler 移除掉 ( 例 : targetElm.onclick = null;) ,避免 memory leak 發生 ( 尤其在 IE) 。 當要寫一段判斷式時 (if, while…etc.) ,最好總是確保括號裡 的陳述是一個 Boolean 值。例如要判斷一個變數是否為一個 字串才做處理,最好寫成 if( typeof str == ’string’ ) ,而不要 寫成 if( str ) 。後者雖然也可 work( 因變數原格式會被自動轉 成 Boolean 格式 ) ,但檢查條件鬆散許多,較容易發生不該出 現的狀況。
  14. 14. 寫 JavaScript 的好習慣 (Good Practice) 儘量減少 event handler(function) 的數量,因為綁定越多的 handler 會消耗更多 memory ”,而降低執行效率。可以使用 Event Delegation“ 的技巧來減少 event handler 的數量。 // Event Delegation Sample document.onclick = function(event){ //IE doesn't pass in the event object event = event || window.event; //IE uses srcElement as the target var target = event.target || event.srcElement; switch(target.id){ case "help-btn": openHelp(); break; case "save-btn": saveDocument(); break; case "undo-btn": undoChanges(); break; //others? } };
  15. 15. 寫 JavaScript 的好習慣 (Good Practice) JavaScript 與 CSS 、 HTML 彼此之間應該儘量減少耦合 (coupling) 程度。 HTML 負責提供內容 (content) , CSS 專職 呈現 (appearance) , JavaScript 則處理行為 (behavior) 。意 即 : 在 HTML 裡不該出現 JavaScript 及 CSS style 的 code , 在 JavaScript 裡不該去處理 CSS style 及 HTML tag 。過度的 耦合會造成 code maintain 上的困難。
  16. 16. 寫 JavaScript 的好習慣 (Good Practice) 當一段程式邏輯需要耗費大量時間運算時,可以試著將可分 割的程式區塊切成數小段,用 setTimeout 指定一小段時間間 隔後再執行。這樣可以讓頁面有更多喘息的時間來回應使用 者的操作行為,而有更好的使用者互動經驗。 // 原本的寫法 function insertItem() { var panel = document.getElementById('my_panel'); for( var i=0; i<100; i++ ) { var item = document.createElement('div'); item.innerHTML = 'Item-'+i; item.onmouseover = function() { this.style['background'] = 'gold'; }; item.onmouseout = function() { this.style['background'] = 'transparent'; }; // ... other handle codes panel.appendChild(item); } } // 新的寫法 var count = 0; function insertItem(index) { if( index >= 100 ) return; var panel = document.getElementById('my_panel'); var item = document.createElement('div'); item.innerHTML = 'Item-'+index; item.onmouseover = function() { this.style['background'] = 'gold'; }; item.onmouseout = function() { this.style['background'] = 'transparent'; }; // ... other handle codes panel.appendChild(item); // delay a while to let browser take a break var method = arguments.callee; window.setTimeout(function() { method(count++); }, 50); }
  17. 17. 寫 JavaScript 的好習慣 (Good Practice) 將 Event Handler 裡面的商業邏輯獨立出來。 Event Handler 應該只處理與 Event 有關資料,例如抓取 keyPress event 的 keyCode 或 event target 。若要利用這些值做一些處理,則改 在別的 function 裡做。不要 pass event object 到 Event Handler 以外的地方。 // 原本的 code function handleKeypress(event) { if( event.keyCode == 13 ) { var target = EventUtil.getTarget(event); var value = 5 * parseInt(target.value); if( value > 10 ) { var elm = document. getElementById('error-msg'); elm.style.display = 'block'; } } } // 改寫後的 code ,將商業邏輯獨立出來 function validateValue(value) { value = 5 * parseInt(value); if( value > 10 ) { var elm = document .getElementById('error-msg'); elm.style.display = 'block'; } } function handleKeypress(event) { if( event.keyCode == 13 ) { var target = EventUtil.getTarget(event); validateValue(target.value); } }
  18. 18. 寫 JavaScript 的好習慣 (Good Practice) 儘量減少 access global variable 的次數可以增進效能,因為 減少了 traverse time 。如果一個 function 裡有數個地方會 access 同一個 global variable( 如 : document) ,可以先用一 個 local variable 去指向 global variable ,後續的 code 再 access local variable ,這樣可加快速度。 // 原本的 code function updateUI() { var imgs = document .getElementsByTagName('img'); for( var i=0, len=imgs.length; i<len, i++ ) { imgs[i].title = document.title + 'image' + i; } var msg = document.getElementById('msg'); msg.innerHTML = 'Update complete'; } // 改寫後的 code ,先宣告一個 local variable 去記 // document function updateUI() { var doc = document; var imgs = doc.getElementsByTagName('img'); for( var i=0, len=imgs.length; i<len, i++ ) { imgs[i].title = doc.title + 'image' + i; } var msg = doc.getElementById('msg'); msg.innerHTML = 'Update complete'; }
  19. 19. 寫 JavaScript 的好習慣 (Good Practice) 如果要建立一個 Storage 來存放資料時,用 Array 會比 Object 快。在 Access 資料的速度上,前者的複雜度為 O(1) ,後者 為 O(n) 。 若一段 code 有很多 if-else 時,改採用 switch 寫法執行速度會 比較快一些。另外可以將比較容易 match 到的 case 條件排在 比較上面,比較少 match 到的 case 排在下面,也會讓效能上 有些許提昇。
  20. 20. 寫 JavaScript 的好習慣 (Good Practice) 儘量減少對 DOM 的操作會使效能有非常顯著的提升, DOM 的運算是 Browser 裡最耗運算資源的。當要對一個 HTML element 連續插入很多或複雜的 child element 時,可以用 DocumentFragment 先將整個 DOM 結構建立好,再一次加進 element ,速度會快很多 ! // 原本的寫法 function insertItem() { var panel = document.getElementById('my_panel'); for( var i=0; i<100; i++ ) { var item = document.createElement('div'); item.innerHTML = 'Item-'+i; item.onmouseover = function() { this.style['background'] = 'gold'; }; item.onmouseout = function() { this.style['background'] = 'transparent'; }; // ... other handle codes panel.appendChild(item); } } // 改寫後的 code ,將 item 寫進 DocumentFragment function insertItem() { var panel = document.getElementById('my_panel'); var fragment = document .createDocumentFragment(); for( var i=0; i<100; i++ ) { var item = document.createElement('div'); item.innerHTML = 'Item-'+i; item.onmouseover = function() { this.style['background'] = 'gold'; }; item.onmouseout = function() { this.style['background'] = 'transparent'; }; fragment.appendChild(item); // ... other handle codes } panel.appendChild(fragment); }
  21. 21. Naming Rule Variable 請用小寫名詞,如 : person, card 。 Function 請用小寫動詞開頭,如 : getName(), pushEvent() 。 假如 Function 會回傳一個 Boolean 值,請用 is 開頭,如 : isEnabled(), isVisible() 。 Constant 請用大寫及底線,如 : STATE, EVENT_TYPE 。 如果是 private 使用,可以在 Variable 及 Function 字首加上底 線。如 : _target, _getElement() 。 HTML Element 的 class name 、寫入 Cookie 或發送 HTTP Request 的參數名稱請用底線 '_' 區隔單字。如 : 'window_panel', 'personal_setting', 'widget_position' 。
  22. 22. Convention Rule 在 JavaScript 裡,字串用單引號標示,在 HTML 裡用雙引號 標示。當規則定下來之後,如果有一天需要把 HTML element 改由 JavaScript 動態產生,就可以直接覆製 HTML code 到 JavaScript 字串變數裡,不需再擔心單雙引號相衝的問題。 縮排 Tab 改用空白取代。這是為了防止當很多人一起改 code 時,程式碼會因為每個人的編輯器不同, Tab 所表示的空白 字元數不同,而導致程式碼縮排不一致變的亂七八糟。 // in JavaScript var str = 'Hello World'; // in HTML <div class=“err-msg“></div> // 假如必須要在 JavaScript 裡直接寫 HTML code var html = '<div class=“err-msg“></div>';
  23. 23. 利用 Ant 將 JavaScript 及 CSS 檔案串接 介紹文章 : http://www.julienlecomte.net/blog/2007/09/16/ 好處 : 將多個 JavaScript 或 CSS 檔案串接成為一個檔案後,可 以減少 Browser 透過網路戴入的次數 (requests) 。進而加快頁 面的瀏覽速度。使用此方法串接 js 檔案時,需注意先後順序, 如同 <script> import js file 一般。 <?xml version="1.0" encoding="UTF-8"?> <project basedir="." default="js.concatenate" name="JS Concatenation"> <!-- the directory to output to --> <property name="build.dir" value="./js" /> <!-- the directory containing the source files --> <property name="src.dir" value="./dev/src" /> <!-- Target to concatenate all js files --> <target name="js.concatenate"> <concat destfile="${build.dir}/concatenated/all.js"> <filelist dir="${src.dir}/js" files="a.js, b.js"/> <fileset dir="${src.dir}/js" includes="**/*.js" excludes="a.js, b.js"/> </concat> </target> </project>
  24. 24. 其他資源 Debuger Firebug Compressor JSMin YUI Compressor Validator JSLint Framework http://en.wikipedia.org/wiki/Comparison_of_JavaScript_framework
  25. 25. JavaScript 推薦書籍 Professional JavaScript for Web Developers, 2/e JavaScript: The Good Parts Pro JavaScript Design Patterns
  26. 26. Thank You!!

×