[文章原始发表:This Is WWW : http://www.plrsoft.cn/blog/?p=69  转载请注明出处] 

     我在上一篇文章“一步一步编写计算器 – 构建和兼容”中在做好第一个简易计算器的时候曾说道:事实上,我们可以做得更加精简,20行代码之内就可以搞定。大家可能觉得很奇怪,你光是JAVASCRIPT就已经达到14行了呀,还有6行,你怎么写得出那10几个显示屏和按键的控件来?

    我要告诉你,完全没问题!

    大家先观察这个代码:

 

  1. <!--content start--> 
  2. <div id="calc"> 
  3.   <!--这里是显示屏 start--> 
  4.     <div id="screen"><input id="result" readonly="readonly" type="text" value="0" /></div> 
  5.   <!--这里是显示屏 end--> 
  6.   <!--这里是功能键 start--> 
  7. <div id="button"> 
  8.   <input id="num_1" onclick="javascript:add_num(this.value)" type="button" value="1" accesskey="A" /> 
  9.   <input id="num_2" onclick="javascript:add_num(this.value)" type="button" value="2" /> 
  10.   <input id="num_3" onclick="javascript:add_num(this.value)" type="button" value="3" /> 
  11.   <input id="num_4" onclick="javascript:add_num(this.value)" type="button" value="4" /> 
  12.   <input id="num_5" onclick="javascript:add_num(this.value)" type="button" value="5" /> 
  13.   <input id="num_6" onclick="javascript:add_num(this.value)" type="button" value="6" /> 
  14.   <input id="num_7" onclick="javascript:add_num(this.value)" type="button" value="7" /> 
  15.   <input id="num_8" onclick="javascript:add_num(this.value)" type="button" value="8" /> 
  16.   <input id="num_9" onclick="javascript:add_num(this.value)" type="button" value="9" /> 
  17.   <input id="num_0" onclick="javascript:add_num(this.value)" type="button" value="0" /> 
  18.   <input id="btn_plus" onclick="javascript:add_btn(this.value)" type="button" value="+" /> 
  19.   <input id="btn_cut" onclick="javascript:add_btn(this.value)" type="button" value="-" /> 
  20.   <input id="btn_multi" onclick="javascript:add_btn(this.value)" type="button" value="×" /> 
  21.   <input id="btn_divid" onclick="javascript:add_btn(this.value)" type="button" value="÷" /> 
  22.   <input id="btn_divid" onclick="javascript:add_btn(this.value)" type="button" value="." /> 
  23.   <input id="btn_ce" onclick="javascript:add_btn(this.value)" type="button" value="CE" /> 
  24.   <input id="=" onclick="javascript:get_result()" type="button" value="=" /></div> 
  25. <!--这里是功能键 end--> 
  26. </div>  

   发现没有?其实,这里面很多代码都是重复的! 仔细观察这俩行!

 

  1. ... 
  2. <input id="num_0" onclick="javascript:add_num(this.value)" type="button" value="0" />    
  3. <input id="btn_plus" onclick="javascript:add_btn(this.value)" type="button" value="+" />  ...

   请告诉我他们的区别! 是的,他们的区别仅在于value属性、id属性以及ONCLICK的动作不同!那就好办了!干重复的活儿是计算机的强项,而人比计算机更强的功能就是,能灵活的让一件重复的事情出现微妙的变化!

   于是,我决定将所有的按键控件都用javascript生成,然后填充到id=button这个DIV中! 所以我们要准备一个初始化函数btn_init(),然后将这个函数放入的ONLOAD事件中代码如下;

 

  1. <div id="main_div"> 
  2.  <!--content start--> 
  3.   <div id="calc"> 
  4.   <!--这里是显示屏 start--> 
  5.        <div id="screen"><input id="result" readonly="readonly" type="text" value="0" /></div> 
  6.   <!--这里是显示屏 end--> 
  7.   <!--这里是功能键 start-->              
  8.         <div id="button"></div>    
  9.    <!--这里是功能键 end--> 
  10. </div> 
  11. <!--content end--> 
  12. </div>  

 

  1. /*---动态构建按钮控件  start---*/    
  2.             function btn_init()     
  3.             {     
  4.                 btn_list=new Array(1,2,3,4,5,6,7,8,9,0,"+","-","×","÷",".","CE","=");  //设置一个数组,存储按键     
  5.                 var button_area =   document.getElementById("button");    //这里就是要写入控件的地方     
  6.    
  7.                 for(var i=0;i<btn_list.length;i++)     
  8.                 {     
  9.    
  10.                     //控件的生成,分为两种,一种是数字,一种是非数字,分别绑定不同的动作     
  11.                     if(isNaN(btn_list[i])) //如果不是数字,则绑定add_btn()事件 否则绑定add_num()     
  12.                     {     
  13.                         //进行一个特殊功能的处理:=       
  14.                         if(btn_list[i]=="="){     
  15.                             button_area.innerHTML+="<input type='button' value='=' id='=' onclick='javascript:get_result()'/>";     
  16.                         }else{     
  17.                             button_area.innerHTML+=" <input type=\"button\" value=\""+btn_list[i]+"\" id=\"btn_"+btn_list[i]+"\" onclick=\"javascript:add_btn(this.value)\"/>";     
  18.                         }     
  19.                     }else{     
  20.                         button_area.innerHTML+=" <input type=\"button\" value=\""+btn_list[i]+"\" id=\"btn_"+btn_list[i]+"\" onclick=\"javascript:add_num(this.value)\"/>";     
  21.                     }     
  22.                 }     
  23.             }     
  24.             /*---动态构建按钮控件  end---*/  

  OK,现在我们的代码生成了! 点这里运行一下! 看,他跑起来了!

  是的,它跑起来了!但是,还远远达不到我们的目标!20行代码!怎么实现?继续观察,我们发现,很多行代码主要花在了判断上,那么我们能不能将一些功能合并呢?比方说,不管按键是数字还是非数字,一律使用“add_btn()”,抛弃掉add_num()和get_result()函数,这样,我们在生成控件环节可以节省很多的代码。同时,将add_btn()、add_num()、get_result()三个函数合并,也能缩减代码数量,也能抛弃掉判断特殊功能的代码(比如要多一个判断语句判断“=”以便分配特殊动作)!

  说干就干,我们先缩短代码生成函数

 

  1. /*---动态构建按钮控件  start---*/    
  2.             function btn_init()     
  3.             {     
  4.                 btn_list=new Array(1,2,3,4,5,6,7,8,9,0,"+","-","×","÷",".","CE","=");  //设置一个数组,存储按键     
  5.                 var button_area =   document.getElementById("button");    //这里就是要写入控件的地方     
  6.                 for(var i=0;i<btn_list.length;i++)     
  7.                 {     
  8.                    button_area.innerHTML+=" <input type=\"button\" value=\""+btn_list[i]+"\" id=\"btn_"+btn_list[i]+"\" onclick=\"javascript:add_btn(this.value)\"/>";     
  9.                  }     
  10.             }     
  11.             /*---动态构建按钮控件  end---*/  

  JUST 11行! 除掉注释,然后将for循环合并成一行,只剩下5行 ,这下清爽了吧!这里5行,加上先前构建最最最简单的计算器的功能实现代码14行,一共是19行!说20行还多呢!

  再让我们来合并add_btn()add_num()get_result()三个函数,同时,我们也进一步的修正前面还存在的一些小BUG,比如,归零后,输入的第一个数字前老会带一个0的囧况!代码如下:

 

  1. is_res=false;    //设定全局变量 is_res     
  2.             function add_btn(act){    //向显示屏添加运算符号     
  3.                 if(is_res==true)     
  4.                 {     
  5.                     result.value="0";    //显示屏归零      
  6.                     is_res=false;       //将IS_RES变量标记为键盘输入状态,表示当前显示屏的数字是输入的,而非运算结果     
  7.                 }     
  8.                 if (act=="CE"){result.value="0";}      
  9.                 else if (act=="="){     
  10.                    /*这里增加对=的特殊处理  亦即合并get_result()函数--*/    
  11.                         var res_temp=result.value;              //设置一个临时变量来进行运算符替换,以免将中间过程显示到屏幕上     
  12.                         res_temp=res_temp.replace(/[×]/g,"*");  //将所有的乘号换成*     
  13.                         res_temp=res_temp.replace(/[÷]/g,"/");  //将所有的除号换成/     
  14.                         result.value=eval(res_temp);     
  15.                         is_res=true;                            //将IS_RES变量标记为结果状态,标示当前显示屏的数字是运算结果,而非输入状态。     
  16.                     /*---合并结束--*/    
  17.                 }     
  18.                 else if(isNaN(act)==false)     
  19.                 {     
  20.                     if(result.value==0){result.value="";}       
  21.                     result.value+=act     
  22.                 }     //合并 add_num(),将数字原本的显示在屏幕上        
  23.                 else {     
  24.    
  25.                     /*----设置一个临时变量来进行正则替换,以免将中间过程显示到屏幕上--*/    
  26.                         var result_temp =result.value+act;        
  27.                         var temp_parr = "";     
  28.    
  29.                     /*---------------寻找整个运算式第一个数字为0且其后所跟的符号不是运算符和小数点的数字---------------*/         
  30.                         var parr1=/(^0+)([0-9])/;                     
  31.                         if(parr1.test(result_temp))     
  32.                         {   //设定替换语句,将整个运算式第一个数字为0开头的数字去除     
  33.                             temp_parr = result_temp.match(parr1)[2];     
  34.                             result_temp=result_temp.replace(parr1,temp_parr);     
  35.                         }     
  36.    
  37.                     /*---------------寻找整个运算式第一个数字为负号 - 的数字---------------*/    
  38.                         var parr2=/(^-*)(0*)(.*)/;                                     
  39.                         if(parr2.test(result_temp))     
  40.                         {             //将非负号的符号去除     
  41.                             temp_parr = result_temp.match(parr2)[1]+result_temp.match(parr2)[3];     
  42.                             result_temp=result_temp.replace(parr1,temp_parr);     
  43.                         }     
  44.    
  45.                     /*---------------将当前运算式中除运算符号外的所有数字取出---------------*/    
  46.                         var parr3=/(?=[+-×÷]){1}([0-9.]+)(?=[+-×÷]){1}/g;          
  47.                         if(parr3.test(result_temp))     
  48.                         {                     
  49.                             temp_parr = result_temp.match(parr3);    //用match方法取出当前式子中的数字     
  50.                             for(var i=0;i<temp_parr.length;i++)     
  51.                             {     
  52.                                 var nozero_parr=Number(temp_parr[i]);       //让每一个数字都成为一个合法数值     
  53.                                 result_temp=result_temp.replace(temp_parr[i],nozero_parr);     
  54.                             }     
  55.                         }                            
  56.    
  57.                     /*---------------寻找算式中连续出现的运算符号,诸如++,x-+等---------------*/    
  58.                         var parr4=/[^0-9]{2,}/g;                          
  59.    
  60.                         if(parr4.test(result_temp))     
  61.                         {                     
  62.                             temp_parr = result_temp.match(parr4);         
  63.                             for(var i=0;i<temp_parr.length;i++)     
  64.                             {     
  65.                                 var cur_sign = new Array(temp_parr[i].substring(0,1),temp_parr[i].substring(1,2));  //取出符号串的前两个符号            //创建一个数组,将取出来的符号串的第一个符号和第二个符号存入一个数组     
  66.                                 var temp_sign="";     
  67.                                 if(cur_sign[1]=="-")     
  68.                                 {     
  69.                                     temp_sign=cur_sign[0]+cur_sign[1];     
  70.                                 }else{     
  71.                                     temp_sign=cur_sign[0];     
  72.                                 }  //如果第二个符号不是负号,那么只保留第一个符号,否则就保留两个符号     
  73.                                 result_temp=result_temp.replace(temp_parr[i],temp_sign);     
  74.                             }     
  75.                         }     
  76.    
  77.                         result.value=result_temp;     
  78.    
  79.                     }     
  80.                 }  

OK,这样,我们就将代码简化完成了! 查看运行效果

很完美!我们在简化的同时,还做了一些工作,那就是为程序的以后扩展做出了铺垫。

一个程序的可扩展性和可方便扩展性是很重要的,比方说,以后如果你要给这个计算器加入开方、乘方、计算正弦余弦的功能,总不能超级麻烦的先在页面添加 input 控件,然后指定一个动作,再去修改JAVASCRIPT吧,那样的代码,只会越来越庞杂。所以,再最开始构建程序的时候,就要考虑到以后如何可以很方便的直接修改JS代码就可以,要基本上做到代码跟页面架构相分离,这种分离有利于页面表现和页面内容的维护。

下一节,将会讲到如何进行功能扩展。