JS簡易計算器——代碼優化

  這裏用一個簡易計算器的案例,來講明代碼的一種優化思路具體方法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類名的(有些直接用標籤名獲取),能夠直接用querySelectorquerySelectorAll(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原則

  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 });

 

第六步:?

  本案例還有能夠繼續改進的地方,例如用模塊化對函數進行再次封裝,添加鍵盤事件,對小數的計算進行改進,等等

相關文章
相關標籤/搜索