一个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();  //这里执行前面定义的初始化函数
})();