web移動開發最佳實務之js篇

來源:互聯網
上載者:User

轉自http://www.cnblogs.com/xiangzi888/archive/2013/01/19/2867630.html

 

一、js概述

  js即JavaScript,是被設計用來驗證表單、檢測瀏覽器、建立cookies、改進設計以及更多應用的網路指令碼語言,它非常容易使用。在web應用中,它是主要的程式設計語言,主要用途是進行各種邏輯控制行為展現等。對於js的最佳化,對於整個應用的提升都是非常顯著的。

二、使用字面量(literal notation)來聲明對象和數組

  建立對象和數組的方法有很多,但是使用字面量是最簡單的。傳統的方法是使用內建的構造器聲明:

//create an objectvar obj = new Object();obj.debug = false;obj.lang = "en"; //create an arrayvar arr = new Array("one", "two", "three");

這種方式在技術上是沒問題的,但是使用字面量聲明會更快而且代碼更少

//create an objectvar obj = {debug: false, lang: "en"}; //create an arrayvar arr = ["one", "two", "three"];

三、避免使用全域變數和函數

  即把屬性方法都綁定到一個命名空間對象裡,這樣不僅可以減少命名衝突,而且可以提升程式效能

  當兩個地區的代碼使用同一個全域變數名作不同用途時,就會產生命名衝突。在JavaScript裡,函數外定義的變數或對象都是全域的,隨著程式碼和庫的增加,命名衝突的機率就越大。如果函數內或其他地區的代碼引用了一個特定的全域變數,指令碼引擎就必須遍曆一遍範圍直到找到這個變數,局部變數則更容易找到。全域變數會在整個指令碼的生命週期中存在,但是局部變數會及時被垃圾收集器回收。

例如以下使用全域的聲明(不高效):

//define global variablesvar lang = "en";var debug = true; //define global functionfunction setLang (arg) {    lang = arg;}

使用如下聲明則更好

var myApp = {    lang: "en",    debug: true,}; myApp.setLang = function (arg) {    this.lang = arg;}

四、高效的使用try catch語句

  你可以使用try-catch語句來攔截程式拋出的錯誤(在瀏覽器處理之前),這對於向使用者隱藏錯誤或者為使用者定製錯誤資訊是很有用的。

  當try結構中發生錯誤時,程式會立即停止跳到catch結構(會提供錯誤對象)中。在catch結構中,錯誤對象會賦給一個新的變數,新的變數在catch結構中一直存在,直到catch語句結束。建立並處理這個新的運行時變數會影響到程式的效能,在關鍵功能迴圈中應避免使用try-catch結構。例如:

var object = ['foo', 'bar'], i;for (i = 0; i < object.length; i++) {   try {      // do something   } catch (e) {      // handle exception   }}

以上這段代碼可能會拋出多個錯誤,這樣寫可能會更好

var object = ['foo', 'bar'], i;try {    for (i = 0; i < object.length; i++) {        // do something    }} catch (e) {    // handle exception}

五、使用賦值運算來連接字串

  字串串連是很常用的操作,也有很多種方式,比如:

//Using the concatenation (+) operatorstr = "h" + "e";  //Using the shorthand assigment (+=) operatorstr += "l";            //Using string.concat()str = str.concat("l", "o");        //Using array.join()str = ["h", "e", "l", "l", "o"].join("");    

如果你執行的串連操作次數較少,那麼以上任何一種方式都可以。但是,當執行大量的串連操作時,就需要最佳化一下了:

//Slower: Concatenating strings with + operatorstr += "x" + "y";

以上串連操作比較,它會按以下步驟執行(參見‘編譯原理’):

  1. 建立一個臨時變數
  2. 串連後的字串xy被賦給這個臨時變數
  3. 臨時變數與str的當前值相加
  4. 結果賦給str變數

你可以使用如下的方式避免使用臨時變數(減少記憶體的使用):

str += "x";str += "y";

六、最佳化你的迴圈

  當你使用迴圈的時候,你可以通過減少每次迭代時工作量來最佳化迴圈的整體效能。例如:

for (var i = 0; i < arr.length; i++) {    // length of arr is recalculated every time}

在以上代碼中,arr.length在每次迴圈中都被計算了一次,這是不必要的,可以聲明一個局部變數len來緩衝這個值,就會提高運行速度:

for (var i = 0, len = arr.length; i < len; i++) {    // cache the length of the array}

或者為了進一步最佳化,考慮反向的執行迴圈(如果不關心數群組成員的順序的話):

for (var i = arr.length; i--;) {    // in reverse}

七、避免使用eval()方法

  eval()方法可以執行一段JavaScript代碼,應該避免使用的原因:

  • 效能較差,它必須調用編譯器來傳遞其參數,然後執行
  • 安全問題,因為它會執行傳遞給它的任何代碼,所以容易受各種注入攻擊,特別是在來源未知的時候
  • 不利於調試,eval的參數是動態產生的,調試起來不方便,可讀性也較差
//Incorrect usage: Using eval to set a valueeval("myValue = myObject." + myKey + ";");//Correct usage: Using subscript notation to set a valuemyValue = myObject[myKey];

另外timeout函數中的setTimeout()和setInterval()也可以接受字串參數,然後執行,因此表現跟eval()一樣。應該避免傳遞字串,如下:

// Incorrect usage: Passing a string to setInterval()var oElement = null;setInterval('oElement = document.getElementById("pepe");', 0);// Correct usage: Passing a function to setInterval()var oElement = null;setInterval(function() {    oElement = document.getElementById("pepe");}, 0);

八、使用事件委託

  在處理DOM事件的時候,你可以僅對一個父元素綁定一個事件而不是每一個子項目。這種技術即事件委託,它利用事件冒泡來分配事件處理常式,可以提高指令碼的效能。比如,一個div元素下面有10個按鈕,你可以給div綁定一個監聽事件,而不是給10個按鈕分別綁定一個事件。傳統的聲明方式:

<a href="javascript:handleClick();">Click</a><button id="btn1" onclick="handleClick();">One</button><button id="btn2" onclick="handleClick();">Two</button>

為了提高代碼的效能,我們可以加一個div父元素,事件會向上冒泡,直到被處理。事件對象是觸發事件的元素,我們可以根據它的id屬性來判斷是哪一個元素觸發了事件:

<div id="btngroup">  <button id="btn1">One</button>  <button id="btn2">Two</button></div>document.getElementById("btngroup").addEventListener("click", function (event) {  switch (event.srcElement.id) {  //firefox 下為 event.target.id  case "btn1":    handleClick();    break;  default:    handleClick();  }}, false); // type, listener, useCapture (true=beginning, false=end)

九、盡量減少DOM操作

  DOM是一個包含了很多資訊的複雜的API,因此即使是很小的操作可能會花費較長的時間執行(如果要重繪頁面的話)。為了提高程式效能,應盡量減少DOM操作,這裡有一些建議:

1.減少DOM的數目

DOM節點的數目會影響與它相關的所有操作,要盡量使DOM樹小一些:

  • 避免多餘的標記和嵌套的表格
  • 元素數盡量控制在500個以內(document.getElementsByTagName('*').length)

2.緩衝已經訪問過的節點

當訪問過一個DOM元素後,就應該把它緩衝起來,因為你的程式往往要重複訪問某個對象的,例如:

for (var i = 0; i < document.images.length; i++) {    document.images[i].src = "blank.gif";}

以上例子中,docum.images對象被訪問了多次,這並不高效,因為每一次迴圈中,瀏覽器都要尋找這個元素兩次:第一次讀取它的長度,第二次改變相應的src值。更好的做法是先把這個對象儲存起來:

var imgs = document.images;for (var i = 0; i < imgs.length; i++) {  //當然也可以把 imgs.length 提前算出來,這裡不是重點    imgs[i].src = "blank.gif";}

十、減少頁面重繪

  在控制DOM元素數目的同時,你還可以通過減少修改元素(減少頁面的重繪)的方法來提高效能。重繪有兩種方式:repaintreflow

1.repaint,也叫redraw,即改變了元素的視覺效果,但是不影響它的排版(比如改變背景顏色)

2.reflow,會影響部分或者全部頁面的排版,瀏覽器不僅要計算該元素的位置,還要計算它影響到的周圍的元素位置

當你要改變頁面配置的時候,reflow就發生了,主要有如下情況:

  • 增加或刪除DOM節點
  • 改變元素的位置
  • 改變元素的尺寸(如margin,padding,border,font,width,height等)
  • 調整瀏覽器視窗的尺寸
  • 增加或刪除css
  • 改變內容(如使用者輸入表單)
  • 命中css選取器(如hover)
  • 更改了class屬性
  • 利用指令碼更改了DOM
  • 檢索一個必須被計算的尺寸(如offsetWidth,offsetHeight)
  • 設定了一個css屬性

這裡有一些減少頁面重繪的建議:

css的建議:

  • 改變class屬性時應盡量少的影響到周圍的元素節點
  • 避免聲明多個內聯的樣式(把多個樣式放在一個外部檔案裡)
  • 有動畫的元素使用絕對位置,這樣不會影響其他元素
  • 避免使用table來排版,如果需要使用儲存資料,那麼要固定排版(table-layout:fixed)

js的建議:

  • 緩衝計算過的樣式
  • 對於固定的樣式,改變class的名詞而不是樣式;對於動態樣式,改變cssText屬性:
    // bad - changing the stle - accessing DOM multiple timesvar myElement = document.getElementById('mydiv');myElement.style.borderLeft = '2px';myElement.style.borderRight = '3px';myElement.style.padding = '5px'; // good - use cssText and modify DOM oncevar myElement = document.getElementById('mydiv');myElement.style.cssText = 'border-left: 2px; border-right: 3px; padding: 5px;';
  • 當你要對一個DOM元素做出很多修改時,可以先進行一些‘預先處理’,批量修改後再替換原始的元素

    • 建立一個副本(cloneNode()),對這個副本進行更新,然後替代原來的節點

      // slower - multiple reflowsvar list = ['foo', 'bar', 'baz'], elem, contents;for (var i = 0; i < list.length; i++) {    elem = document.createElement('div');    content = document.createTextNode(list[i]);    elem.appendChild(content);    document.body.appendChild(elem); // multiple reflows}             // faster - create a copyvar orig = document.getElementById('container'),    clone = orig.cloneNode(true), // create a copy    list = ['foo', 'bar', 'baz'], elem, contents;clone.setAttribute('width', '50%');
    • 修改一個不可見的元素,可以先讓其不可見(display:none),修改完成後,再恢複其可見(display:block),這樣就會減少reflow的次數
      // slowervar subElem = document.createElement('div'),    elem = document.getElementById('animated');elem.appendChild(subElem);elem.style.width = '320px';             // fastervar subElem = document.createElement('div'),    elem = document.getElementById('animated');elem.style.display = 'none'; // will not be repaintedelem.appendChild(subElem);elem.style.width = '320px';elem.style.display = 'block';
    • 建立一個文檔片段(使用DocumentFragment()),修改完成後,再把它追加到原始文檔中
      // slowervar list = ['foo', 'bar', 'baz'], elem, contents;for (var i = 0; i < list.length; i++) {    elem = document.createElement('div');    content = document.createTextNode(list[i]);    elem.appendChild(content);    document.body.appendChild(elem); // multiple reflows}             // fastervar fragment = document.createDocumentFragment(),    list = ['foo', 'bar', 'baz'], elem, contents;for (var i = 0; i < list.length; i++) {    elem = document.createElement('div');    content = document.createTextNode(list[i]);    fragment.appendChild(content);}document.body.appendChild(fragment); // one reflow

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.