JS 之简单计算器

时间:2022-12-29 14:25:32

要求:

响应用户对数字和算术操作符按钮的操作,记录并显示用户通过按钮输入的算术表达式(50分)响应用户功能按钮的操作

用户按下“←”按钮,删除当前算术表达式最后一个字符,并更新显示

用户按下“CE”按钮,清除当前算术表达式(20分)用户按下“=”按钮,计算当前表达式的结果并显示

如果,算术表达式非法,弹出警告框提醒用户,并终止计算

效果图:

JS 之简单计算器

代码链接:https://github.com/sysuKinthon/Web2.0/tree/master/Web2.0/calculator

经验:

1)其实代码实现不难,而且用了已经不大推崇的eval函数,这个函数会带来代码注入的问题;

2)实现过程中主要想说的是一种思维,因为之前一直都不怎么接触事件处理机制,一直都是用c,c++,java来打代码,所以在写这个的时候有一个致命的思维误区:

    //if input the symbol, the reslut should be clear;
    for(i = 0; i < symbolList.length; ++i) {
        symbolList[i].onclick = function(event) {  //不能使用i来索引对象,因为当Onclick事件发生时,i已经都是20了。
            if(flag == true) {
                expression.value = "";
                flag = false;
            }
            var content = event.target.innerText;
            expression.value = expression.value + content;
        }
    }

一开始在for循环里面,一上手就想用sybmolList[i]来引用innerText;也就是:

var content = event.target.innerText;
换成
var content = symbolList[i].innerText;

发现我一点击事件就出现越界行为,输出查看i,i的值都是同一个数,这个数决定于window.onload回调函数结束后的i值。要清楚,这是闭包导致的,当我们的DOM加装完毕后,window.onload事件就发动了,并调用事件处理;好像一切都没有问题,但是想想看,当我们点击事件,并触发了symbolList中的对象时,回调了我们设置的函数,此时它引用i的值的话,必然是symbolList.length,因为onload事件已经执行了,但由于闭包,如果我们的点击函数中有引用外部的变量的话,是可以访问到的,在javascript中是采用了垃圾处理机制,闭包包含了外部的活动对象,所以这个对象还没有被销毁,可以被引用。所以我们在程序中是不能直接调用 i来索引事件源的,要使用event.target

拓展:

毕竟利用eval来实现计算会导致一些意想不到的问题,所以就用算法实现了下中缀表达式的计算:参考

关于考虑运算符优先级的问题,也就是什么时候栈里面的运算符应该出栈,主要考虑一个方面就可以了,就是运算符的栈始终要保持栈顶的元素优先级最高,相同优先级的后面进入的优先级高,同时需要考虑对于")"来说,当在遇到"("时,它的优先级是最高的,遇到后,就是最低的了(也就是不要入栈)。

主要的函数代码如下:

 1 // calculator the postfix expression
 2 function calculate(postfix) {
 3     var i, item, result;
 4     var num = [];
 5     for(i = 0; i < postfix.length; i++) {
 6         item = parseFloat(postfix[i]);
 7         if(!isNaN(item)) {
 8             num.push(item);
 9         } else {
10             second = num.pop();
11             first = num.pop();
12             switch(postfix[i]) {
13                 case '+':
14                     result = first + second;
15                     num.push(result);
16                     break;
17                 case '-':
18                     result = first - second;
19                     num.push(result);
20                     break;
21                 case '/':
22                     result = first / second;
23                     num.push(result);
24                     break;
25                 case '*':
26                     result = first * second;
27                     num.push(result);
28                     break;
29             }
30             //console.log(result);
31         }
32     }
33     result = num.pop();
34     result = parseFloat(result.toFixed(8), 10);
35     return result;
36 }
37 
38 
39 // translate the infix expression to the postfix expression
40 function toPostfix(infix) {
41     //split the input to the token
42     var regex = /(\(|\)|\/|\*|\-|\+)/;
43     var array= infix.split(regex);
44     var i;
45     var postfix = [];
46     var symbol = [];
47     // remove the empty item
48     for(i = 0; i < array.length; i++) {
49         if(array[i] == "")
50             array.splice(i, 1);
51     }
52 
53     for(i = 0; i < array.length; i++) {
54         var item = array[i];
55         if(!isNaN(parseFloat(item))) {
56             postfix.push(item);
57         } else {
58             while(symbol.length) {
59                 if(compare(item, symbol[symbol.length-1])) { //if the item is greater
60                     break;
61                 } else {
62                     postfix.push(symbol.pop());
63                     /*if(postfix[postfix.length-1] == "(")
64                         console.log(1);*/
65                 }
66             }
67             if(item == ")") {
68                 symbol.pop(); //pop "("
69             } else { 
70                 symbol.push(item);
71             }
72         }
73     }
74     while(symbol.length) {
75         if(symbol) {
76             postfix.push(symbol.pop());
77         }
78     }
79     return postfix;
80 }
81 
82 function compare(item, top) {
83     if(item.match(/\)/) && top.match(/\(/))
84         return true;
85     if(item.match(/\(/) || top.match(/\(/)) {
86         return true;
87     }
88     if(item.match(/\*|\//) && top.match(/\+|\-/)) {
89         return true;
90     }
91     //console.log(item);
92     return false;
93 }