這裏用一個簡易計算器的案例,來講明代碼的一種優化思路和具體方法html
先放上該項目的HTML和CSS部分框架
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>簡易計算器</title> 8 <style> 9 body{ 10 background:#777777; 11 } 12 section{ 13 width:500px;height:300px; 14 position:absolute;top:50%;left:50%; 15 margin-top:-150px;margin-left:-250px; 16 border:1px dashed black; 17 font-size:20px; 18 } 19 p{ 20 text-align:center; 21 margin-top:50px; 22 } 23 button{ 24 width:40px;height:40px; 25 } 26 </style> 27 </head> 28 <body> 29 <section> 30 <p> 31 <input type="text" class="input_forward" value="0"> 32 <span class="symbol">+</span> 33 <input type="text" class="input_backward" value="0"> 34 <span>=</span> 35 <span class="result">0</span> 36 </p> 37 <p> 38 <button title="add">+</button> 39 <button title="subtract">-</button> 40 <button title="multiply">×</button> 41 <button title="divide">÷</button> 42 </p> 43 </section> 44 </body> 45 </html>
效果圖以下(關注的是實際應用效果,所以CSS樣式不作太多調整):ide
最開始先實現效果,用最簡單的思路便可(目的是先實現,先完成後完善):模塊化
①獲取元素:這裏大多元素都是有獨立的class類名的(有些直接用標籤名獲取),能夠直接用querySelector和querySelectorAll(button按鈕)獲取函數
②封裝函數:加減乘除,經過獲取的元素value,計算後改變result的值,同時改變符號位優化
③綁定事件:給每個button綁定事件,在點擊時執行某一函數ui
這裏直接把結構、樣式和行爲分離,不在HTML標籤內部添加JS事件,而是在script標籤中綁定事件this
JS部分的代碼以下:spa
1 //獲取全部元素 2 var num_forward = document.querySelector(".input_forward"), 3 num_backward = document.querySelector(".input_backward"), 4 symbol = document.querySelector(".symbol"), 5 result = document.querySelector(".result"), 6 btns = document.querySelectorAll("button"); 7 //封裝全部函數 8 function addHandler(){ 9 var num1 = num_forward.value, 10 num2 = num_backward.value; 11 symbol.innerText = "+"; 12 result.innerText = +num1 + +num2;//這裏是爲了防止被當成字符串拼接 13 } 14 function subtractHandler(){ 15 var num1 = num_forward.value, 16 num2 = num_backward.value; 17 symbol.innerText = "-"; 18 result.innerText = num1 - num2; 19 } 20 function multiplyHandler(){ 21 var num1 = num_forward.value, 22 num2 = num_backward.value; 23 symbol.innerText = "×"; 24 result.innerText = num1 * num2; 25 } 26 function divideHandler(){ 27 var num1 = num_forward.value, 28 num2 = num_backward.value; 29 symbol.innerText = "÷"; 30 result.innerText = num1 / num2; 31 } 32 //綁定事件 33 btns[0].onclick = addHandler; 34 btns[1].onclick = subtractHandler; 35 btns[2].onclick = multiplyHandler; 36 btns[3].onclick = divideHandler;
通過觀察得知,其中的綁定事件至關於遍歷了btns裏的全部元素,所以綁定事件能夠用循環來改寫code
JS代碼以下:
1 //獲取全部元素 2 var num_forward = document.querySelector(".input_forward"), 3 num_backward = document.querySelector(".input_backward"), 4 symbol = document.querySelector(".symbol"), 5 result = document.querySelector(".result"), 6 btns = document.querySelectorAll("button"); 7 //封裝全部函數 8 function addHandler(){ 9 var num1 = num_forward.value, 10 num2 = num_backward.value; 11 symbol.innerText = "+"; 12 result.innerText = +num1 + +num2;//這裏是爲了防止被當成字符串拼接 13 } 14 function subtractHandler(){ 15 var num1 = num_forward.value, 16 num2 = num_backward.value; 17 symbol.innerText = "-"; 18 result.innerText = num1 - num2; 19 } 20 function multiplyHandler(){ 21 var num1 = num_forward.value, 22 num2 = num_backward.value; 23 symbol.innerText = "×"; 24 result.innerText = num1 * num2; 25 } 26 function divideHandler(){ 27 var num1 = num_forward.value, 28 num2 = num_backward.value; 29 symbol.innerText = "÷"; 30 result.innerText = num1 / num2; 31 } 32 //用循環綁定事件 33 for(var i=0;i<btns.length;i++){ 34 btns[i].onclick = function(){ 35 switch(this.title){ 36 case "add": 37 addHandler(); 38 break; 39 case "subtract": 40 subtractHandler(); 41 break; 42 case "multiply": 43 multiplyHandler(); 44 break; 45 case "divide": 46 divideHandler(); 47 break; 48 } 49 } 50 }
進一步觀察,能夠發現加減乘除四個函數裏都分紅三個部分:
①改變符號位的符號
②計算得出結果
③把計算的結果賦值到結果區域
所以把這三部分提出來,單獨封裝三個通用的函數,主要是爲了減小函數內部的細節暴露,便於閱讀函數
該部分修改後,JS代碼以下:
1 //獲取全部元素 2 var num_forward = document.querySelector(".input_forward"), 3 num_backward = document.querySelector(".input_backward"), 4 symbol = document.querySelector(".symbol"), 5 result = document.querySelector(".result"), 6 btns = document.querySelectorAll("button"); 7 //封裝全部函數 8 //計算用的函數 9 function add(num1,num2){ 10 return +num1 + +num2; 11 } 12 function subtract(num1,num2){ 13 return num1 - num2; 14 } 15 function multiply(num1,num2){ 16 return num1 * num2; 17 } 18 function divide(num1,num2){ 19 return num1 / num2; 20 } 21 //修改符號位的函數 22 function changeSymbol(ele){ 23 symbol.innerText = ele; 24 } 25 //修改輸出內容的函數 26 function changeResult(ele){ 27 result.innerText = ele; 28 } 29 function addHandler(){ 30 var num1 = num_forward.value, 31 num2 = num_backward.value; 32 changeSymbol("+"); 33 changeResult(add(num1,num2));//這裏是爲了防止被當成字符串拼接 34 } 35 function subtractHandler(){ 36 var num1 = num_forward.value, 37 num2 = num_backward.value; 38 changeSymbol("-"); 39 changeResult(subtract(num1,num2)); 40 } 41 function multiplyHandler(){ 42 var num1 = num_forward.value, 43 num2 = num_backward.value; 44 changeSymbol("×"); 45 changeResult(multiply(num1,num2)); 46 } 47 function divideHandler(){ 48 var num1 = num_forward.value, 49 num2 = num_backward.value; 50 changeSymbol("÷"); 51 changeResult(divide(num1,num2)); 52 } 53 //用循環綁定事件 54 for(var i=0;i<btns.length;i++){ 55 btns[i].onclick = function(){ 56 switch(this.title){ 57 case "add": 58 addHandler(); 59 break; 60 case "subtract": 61 subtractHandler(); 62 break; 63 case "multiply": 64 multiplyHandler(); 65 break; 66 case "divide": 67 divideHandler(); 68 break; 69 } 70 } 71 }
目前爲止,書寫的JS代碼中,存有太多的全局變量以及函數,觀察能夠發現,全部的元素都是最外部框架的子元素,所以能夠用一個對象包裹起來
同時,也能夠用一個對象把功能近似的函數給包裹起來,用訪問對象方法的方式來調用函數
修改後的JS代碼以下:
1 //獲取全部元素 2 var section = document.querySelector("section"); 3 var calculatorEles = { 4 num_forward :section.querySelector(".input_forward"), 5 num_backward:section.querySelector(".input_backward"), 6 symbol:section.querySelector(".symbol"), 7 result:section.querySelector(".result"), 8 btns:section.querySelectorAll("button") 9 } 10 //封裝全部函數 11 //計算用的函數 12 var operation = { 13 add:function(num1,num2){ 14 return +num1 + +num2; 15 }, 16 subtract:function(num1,num2){ 17 return num1 - num2; 18 }, 19 multiply:function(num1,num2){ 20 return num1 * num2; 21 }, 22 divide:function(num1,num2){ 23 return num1 / num2; 24 } 25 } 26 //修改符號位的函數 27 function changeSymbol(ele){ 28 calculatorEles.symbol.innerText = ele; 29 } 30 //修改輸出內容的函數 31 function changeResult(ele){ 32 calculatorEles.result.innerText = ele; 33 } 34 function addHandler(){ 35 changeSymbol("+"); 36 changeResult(operation.add(calculatorEles.num_forward.value,calculatorEles.num_backward.value));//這裏是爲了防止被當成字符串拼接 37 } 38 function subtractHandler(){ 39 changeSymbol("-"); 40 changeResult(operation.subtract(calculatorEles.num_forward.value,calculatorEles.num_backward.value)); 41 } 42 function multiplyHandler(){ 43 changeSymbol("×"); 44 changeResult(operation.multiply(calculatorEles.num_forward.value,calculatorEles.num_backward.value)); 45 } 46 function divideHandler(){ 47 changeSymbol("÷"); 48 changeResult(operation.divide(calculatorEles.num_forward.value,calculatorEles.num_backward.value)); 49 } 50 //用循環綁定事件 51 for(var i=0;i<calculatorEles.btns.length;i++){ 52 calculatorEles.btns[i].onclick = function(){ 53 switch(this.title){ 54 case "add": 55 addHandler(); 56 break; 57 case "subtract": 58 subtractHandler(); 59 break; 60 case "multiply": 61 multiplyHandler(); 62 break; 63 case "divide": 64 divideHandler(); 65 break; 66 } 67 } 68 }
OCP原則,即開放封閉原則(Open Close Principle),大體上即開放接口,封閉原有的代碼(防止修改時原有代碼損壞)
到目前爲止的代碼,尚存在如下問題:
①switch語句,通常不使用switch,由於實際開發時極可能不給源代碼,增刪改查時沒法在原有的switch內部修改
②每一個button點擊時觸發的函數,實際上能夠提取出來,直接放到for循環中(修改符號位能夠識別button中的innerText,而具體調用的函數名稱也在每一個button的title裏體現了)
③計算方法的問題,既然能夠用button的innerText來識別符號位,那也能夠用button的title來識別計算方法,並調用這個方法
④添加新方法的問題,相似於①,實際開發中,極可能不會提供源碼,那若是須要添加新的計算方法,是沒法深刻到對象內部進行添加的,所以有必要寫一個接口,留給將來添加新方法用
特別注意:這裏不適合直接給對象添加新屬性來添加新方法,由於不知道對象內部是否已經有同名的屬性
綜合以上,修改完成的JS代碼以下:
1 //獲取全部對象 2 var cal = document.querySelector("section"); 3 var calculatorEles = { 4 input_first :cal.querySelector(".input_first"), 5 input_second:cal.querySelector(".input_second"), 6 symbol:cal.querySelector(".symbol"), 7 result:cal.querySelector(".result"), 8 btns:cal.querySelectorAll("button") 9 }; 10 //讓元素內全部元素綁定同一事件 11 function each(array,fn){ 12 for(var i=0;i<array.length;i++){ 13 fn(i,array[i]); 14 } 15 } 16 //變動符號位內容 17 function changeSymbol(ele){ 18 calculatorEles.symbol.innerText = ele; 19 } 20 //變動輸出內容 21 function changeResult(put){ 22 calculatorEles.result.innerText = put; 23 } 24 //加減乘除(大函數中的小函數封裝) 25 var operation = { 26 add:function (num1,num2){ 27 return +num1 + +num2; 28 }, 29 subtract:function(num1,num2){ 30 return num1 - num2; 31 }, 32 multiply:function(num1,num2){ 33 return num1 * num2; 34 }, 35 divide:function(num1,num2){ 36 return num1 / num2; 37 }, 38 //添加一個用來添加新方法的接口 39 addOperation:function(name,fn){ 40 if(!this[name]){ 41 this[name] = fn;//只有當前對象裏不含有這個名稱的方法時,纔會添加方法 42 } 43 return this; 44 } 45 } 46 //運算函數 47 function operate(name,num1,num2){ 48 if(!operation[name]){ 49 throw new Error("沒有名爲"+name+"的計算方法!"); 50 } 51 operation[name](num1,num2); 52 } 53 each(calculatorEles.btns,function(index,ele){ 54 ele.onclick = function(){ 55 changeSymbol(this.innerText); 56 changeResult(operate(this.title,calculatorEles.input_first.value,calculatorEles.input_second.value)); 57 } 58 });
本案例還有能夠繼續改進的地方,例如用模塊化對函數進行再次封裝,添加鍵盤事件,對小數的計算進行改進,等等