一个JavaScript计算器
   可以实现两操作数的运算及开方、取余等,如果下式7-3*2,将在点*时,先计算出7-3=4,然后*2,计算结果为8而不是1。源程序见附件。核心代码如下:
( function(){

   //这里是定义一个控制器,用来对外开放计算接口和拦截计算中出现的异常
  oController = {
    addNumber : function(tok){ if(errorState()) return errorResult(); else return addNumber(tok)},
    addOper :     function(tok){ if(errorState()) return errorResult(); else return addOper(tok)},
    doFun :     function(tok){ if(errorState()) { return errorResult();} else return doFun(tok)},
    cleanError : cleanError
  }

   //用来保存记忆的变量
   var memery = 0;
    
   //这是符号对应的计算规则,这里采用一个闭包的对照表来处理
   var opMap = {
     "+": function(a,b){ return b + a},   //处理加法运算的闭包
     "-": function(a,b){ return b - a},   //处理减法运算的闭包
     "*": function(a,b){ return b * a},   //处理乘法运算的闭包
     "/": function(a,b){ return b / a},   //处理除法运算的闭包
     "=": function(a,b){ return a},     //处理最终结果
     "C":init,               //清零    
     "CE":init,               //清零
     "sqrt": function(a){ return Math.sqrt(a)},   //计算开方
     "1/x": function(a){ return 1/a},       //计算倒数
     "%": function(a){ return a/100},       //求余数
     "+/-": function(a){ return -a},       //正负号
     "MS": function(a){memery = a; return a},   //记忆
     "M+": function(a){memery += a; return a},   //累加记忆
     "MR": function(a){ return memery},     //从记忆中读取
     "MC": function(a){memery = 0; return a}   //清除记忆
  }
    
   //用来存储数值、操作符和输入缓存的数据结构
   var oMemery = {
    numStack : [],   //存储数值
    operStack : [],   //存储字符串
    inBuffer : ""   //输入显示缓存
  }

   //检测计算中出现的数字异常,若有,返回给控制器进行处理
   function errorState()
  {
     with(oMemery)
    {
       var n = numStack[numStack.length - 1];

       return n == Infinity || isNaN(n);
    }
  }
   //若出现异常,这个函数提供出现异常时的数值结果(数值堆栈顶的值)
   function errorResult()
  {
     with(oMemery)
    {
       return formatBuff(numStack[numStack.length - 1]);
    }
  }
   //清除异常并从错误中恢复
   function cleanError()
  {
     with(oMemery)
    {
      numStack[numStack.length - 1] = 0;
    }
  }

   function init()     //初始化
  {
     with(oMemery)
    {
      numStack.length = 0;   //清空数值堆栈
      operStack.length = 0;   //清空操作符堆栈
      numStack.push(0);   //在数值堆栈中推入一个0作为栈顶
      inBuffer = "";   //清空输入缓存
       return inBuffer;   //将清空后的缓存值(实际上是空字符串'')返回
    }
  }

   function doOper()     //计算表达式
  {
     with(oMemery)
    {
       if(operStack.length) //如果运算符堆栈中有值
      {
         try
        {
           //取得栈顶运算符对应的操作方法
           var op = opMap[operStack.pop()];    
           var args = [];

           //该方法需要提供几个操作数,可以通过检查op.length得到
           for( var i = 0; i < op.length; i++)    
          {
             //从数值堆栈中依次取相应的操作数进行处理
            args.push(numStack.pop());    
          }
           //在这里实际进行计算,并把计算结果重新压入堆栈
          numStack.push(op.apply( this, args));    
        }
         catch(ex)
        {
          alert(ex.message);
        }
      }
       return numStack[numStack.length - 1];
    }
  }
    
   //格式化显示的数值,主要是为了符合计算器的习惯,比如0显示成0.(带小数点)
   function formatBuff(buf)
  {
     if(buf == "")    
       return "0.";
     else{
      buf = parseFloat(buf);
       return /\./.test(buf) ? buf : buf + ".";
    }
  }

   function addNumber(tok)     //输入数值
  {
     with(oMemery)
    {
       try
      {
         var token;
         if(tok == "\b") //如果输入的是一个退格
          token = inBuffer.slice(0,-1);   //那么把缓存中的内容去掉一个
         else    
          token = inBuffer + tok.toString();   //否则接受新输入的数字
         //如果数值的第一位是小数点,显示的时候要补一个0
         if(token.slice(0,1) == ".") token = 0 + token;
         //判断输入接收后的结果是否满足数值的格式:^([\d]+(\.)?[\d]*)?$,其他
                                                                 //常用正则表达式可以参考我的《C#中验证控件的使用方法总结》的博文
         if(/^([\d]+(\.)?[\d]*)?$/.test(token))
        {
          inBuffer = token;   //如果满足,则确认接受,写入缓存
        }

         return formatBuff(inBuffer);
        
      }
       catch(ex)
      {
        alert(ex.message);
      }
    }
  }

   function addOper(tok) //输入运算符
  {
     with(oMemery)
    {
       try
      {
         //如果缓存中有数值,将它推入数值堆栈
         if(inBuffer != "")    
          numStack.push(parseFloat(inBuffer));
         //否则从操作符堆栈中将前一个输入的操作符弹出用当前操作符替代
         else
          operStack.pop();    
         var ret = doOper();   //计算表达式
        operStack.push(tok);   //将新输入的运算符推入堆栈
        inBuffer = "";   //清空输入缓存
         return formatBuff(ret);
      }
       catch(ex)
      {
        alert(ex.message);
      }
    }
  }

   function doFun(tok) //处理函数
  {
     with(oMemery)
    {
       try{
         //假如是一些函数如sqrt
         var fun = opMap[tok];
         //如果输入缓存无内容
         if(inBuffer == "")    
          inBuffer = numStack.pop(); //从数值堆栈中取数
         else
          operStack.push(tok);   //否则将函数推入操作符堆栈

         //计算函数调用结果并放入数值堆栈
        numStack.push(fun(parseFloat(inBuffer)));    

        inBuffer = ""; //清空缓存

         return formatBuff(numStack[numStack.length - 1]);
      }
       catch(ex){
        alert(ex.message);
      }
    }
  }
    
  init();   //这里执行前面定义的初始化函数
})();