1. 程式人生 > >《JavaScript高階程式設計》事件及最佳實踐簡記

《JavaScript高階程式設計》事件及最佳實踐簡記

  • 5種類型:undefined, boolean, string, number, object
    • 使用typeof結果新增:null, function
  • ECMAScript5中為陣列提供五種迭代方法
    • every(), filter(), forEach(), map(), some()
    • every和some:陣列中資料是否滿足條件,return true/false
  • 使用函式的值取代函式名
    • 如下錯誤
    		function(){
    			//這裡是塊級作用域
    		}()
    ; //出錯
  • 可修改為如下:
    		(function(){
    			//這裡是塊級作用域
    		})();  //正確
    
  • DOM1沒有規定事件處理
    • DOM0級,繫結click方法,只能單個事件
    • DOM2級,addEventListener(),可以多個事件
      • 包含三個階段:事件捕獲階段、處於目標階段、事件冒泡階段
    • DOM3級,包括以下及部分內容
      • UI事件
        • load, unload, select, resize
      • 焦點事件
        • focus
      • 滑鼠事件
        • mousedown
        • mouseup
        • click
        • mousedown
        • mouseup
        • click
        • dbclick
      • 滾輪事件
        • mousewheel
      • 鍵盤事件
        • keydown
        • keypress
        • keyup
      • 文字事件
        • textInput
      • 複合事件
        • 僅有IE9+支援
      • 變動事件
        • DOM中的某一部分發生變化時給出提示
  • JavaScript最佳實踐
    • 可維護性
      • 可維護的程式碼
        • 可理解性
        • 直觀性
        • 可適應性
        • 可擴充套件性
        • 可除錯性
      • 程式碼約定
        • 可讀性
        • 變數和函式命名
        • 變數型別透明
          • 使用合適的命名方式
          • 匈牙利標記法
            • o:物件
            • s:字串
            • i:整數
            • f:浮點數
            • b:布林型
          • 型別註釋
      • 鬆散耦合
        • 解耦HTML/JavaScript
        • 解耦CSS/JavaScript
        • 解耦應用邏輯/事件處理程式
      • 程式設計實踐
        • 尊正物件所有權
          • 簡單的說:如果不負責建立或維護某個物件,就不能對它們進行修改
          • 具體地說:
            • 不要為例項或原型新增屬性
            • 不要為例項或原型新增方法
            • 不要重定義已存在的方法
        • 避免全域性量
        	//兩個全域性量——避免!!
        	var name = "Nicholas"'
        	function sayName(){
        		Alert(name);
        	}
        	
        	//一個全域性量——推薦
        	var MyApplication = {
        		name : "Nicholas",
        		sayName : function(){
        			alert(this.name);
        		}
        	};
        
        • 避免與null進行比較
          • 如果值應為一個引用型別,使用instanceof操作符檢查其建構函式
          • 如果值應為一個基本型別,使用typeof檢查其型別
          • 如果是希望物件包含某個特定的方法名,則使用typeof操作符確保指定名字的方法存在於物件上。
          • 程式碼中的null比較越少,就越容易確定程式碼的目的,並消除不必要的錯誤
        • 使用常量
          • z定義Constants類,編寫常量key/value值
          • 重複值
          • 使用者介面字串
          • URLs
          • 任意可能會更改的值
    • 效能
      • 注意作用域
        • 避免全域性查詢(涉及作用域鏈上的查詢)
        • 避免with語句
      • 選擇正確方法
        • 避免不必要的屬性查詢(一次查詢複雜度O(n))
          • 一旦多次用到物件屬性,應該將其儲存在區域性變數中
        • 優化迴圈
          • 減值迭代(很多情況下更高效)
          • 簡化終止條件
          • 簡化迴圈體
          • 使用後測試迴圈(避免第一次的判斷)
        • 展開迴圈
          • 當迴圈次數是確定的,消除迴圈並使用多次函式呼叫往往更快
            • 可以消除建立迴圈和處理終止條件的額外開銷
          • 若迭代次數不能事先確定,可以考慮使用一種叫Duff裝置的技術
      		 // Jeff Greenberg for JS implementation of Duff's Device
      	  // 假設:values.length 0
      	  function process(v) {
      	      alert(v);
      	  }
      	  var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
      	  var iterations = Math.ceil(values.length / 8);
      	  var startAt = values.length % 8;
      	  var i = 0; 
      	  do {
      	      switch(startAt) {
      	          case 0 : process(values[i++]);
      	          case 7 : process(values[i++]);
      	          case 6 : process(values[i++]);
      	          case 5 : process(values[i++]);
      	          case 4 : process(values[i++]);
      	          case 3 : process(values[i++]);
      	          case 2 : process(values[i++]);
      	          case 1 : process(values[i++]);
      	      }
      	      startAt = 0;
      	  }while(--iterations 0);
      
      • 達夫裝置背後的基本理念是:
        • 每次迴圈中最多可8次呼叫process()函式,迴圈迭代次數為元素總數除以8
        • 因為總數不一定是8的整數倍,所以startAt變數存放餘數,指出第一次迴圈中應當執行多少次process()。
        • 比方說現在有12個元素,那麼第一次迴圈將呼叫process()4次,第二次迴圈呼叫process()8次,用2次迴圈代替了12次迴圈。
        • tips: case語句找到第一個匹配的語句之後,未遇見break語句時,後面的語句將會依次執行。
      • 03年改進版本,先處理餘數部分,幾乎增速40%
        	// Speed Up Your Site(New Riders, 2003)
        	  function process(v) {
        	      alert(v);
        	  }    
        	  var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
        	  var iterations = Math.floor(values.length / 8);
        	  var leftover = values.length % 8;
        	  var i = 0; 
        	  if(leftover 0) {
        	      do {
        	          process(values[i++]);
        	      }while(--leftover 0);
        	  }    
        	  do {
        	      process(values[i++]);
        	      process(values[i++]);
        	      process(values[i++]);
        	      process(values[i++]);
        	      process(values[i++]);
        	      process(values[i++]);
        	      process(values[i++]);
        	      process(values[i++]);
        	  }while(--iterations 0);
        
    • 避免雙重解釋
      • 在以下例子中,都要解析包含JavaScript程式碼的字串
      • 這是不能再初始的解析過程中完成的,因為程式碼是包含字串中的。
      • 必須在程式碼執行的同時新啟動一個解析器來解析
      • 例項化一個新的解析器有不容忽視的開銷
      	//某些程式碼求值——避免!!
      	eval("alert('Hello world!')");
      	//某些程式碼求值——已修正
      	alert('Hello world!');
      	
      	//建立新函式——避免!!
      	var sayHi = new Function("alert('Hello world!')");
      	//建立新函式——已修正
      	var sayHi = function(){
      		alert('Hello world!');
      	};
      	//設定超時——避免!!
      	setTimeout("alert('Hello world!')", 500);
      	//設定超時——已修正
      	setTimeout(function(){
      		alert('Hello world!');
      	}, 500);
      
    • 效能的其他注意事項
      • 原生方法較快
      • Switch語句較快
      • 位運算子較快
  • 最小化語句數
    • JavaScript程式碼中語句數量也影響所執行的操作的速度
    • 完成多個操作的單個語句要比完成單個操作的多個語句快
    1. 多個變數宣告
    	//4個語句——很浪費
    	var count = 5;
    	var color = "blue";
    	var values = [1, 2, 3];
    	var now = new Date();
    	
    	//一個語句
    	var count = 5,
    		color = "blue";
    		values = [1, 2, 3];
    		now = new Date();
    
    1. 插入迭代值
    	var name = values[i++];
    
    1. 使用陣列和物件字面量
    	//用4個語句建立和初始化陣列——浪費
    	var values =  new Array();
    	values[0] = 123;
    	values[1] = 456;
    	values[2] = 789;
    	//用1個語句建立和初始化陣列
    	var values =  [123, 456, 789];
    	
    	//用4個語句建立和初始化物件——浪費
    	var person = new Object();
    	person.name = "nicholas";
    	person.age = 29;
    	person.sayName = function(){
    		alert(this.name);
    	}
    	//用1個語句建立和初始化物件
    	var person = {
    		name = "nicholas",
    		age = 29,
    		sayName = function(){
    			alert(this.name);
    		}
    	}
    
  • 優化DOM互動
    1. 最小化現場更新(部分更新)
    2. 使用innerHTML
      使用時構建好一個字串然後一次性呼叫innerHTML要比呼叫innerHTML多次快得多
    3. 使用事件代理
      可以將事件處理程式附加到更高層的地方(祖先節點)負責多個目標的事件處理。
    4. 減少呼叫HTMLCollection次數