61,111
社区成员
发帖
与我相关
我的任务
分享
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>计算器</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<div class="container">
<div class="screen">
<textarea id="inuptScreen"></textarea>
<!--输入面板-->
<input type="text" id="outputScreen" />
<!--结果面板-->
</div>
<div class="input">
<table>
<tr>
<td>
<input type="button" name="off" value="OFF" onclick="off()" />
</td>
<td>
<input type="button" name="del" value="DEL" onclick="del()" />
</td>
<td>
<input type="button" name="clr" value="CLR" onclick="clr()" />
</td>
<td>
<input type="button" name="pi" value="π" onclick="disp(this)" />
</td>
<td>
<input type="button" name="equal" value="=" onclick="equal()" />
</td>
</tr>
<tr>
<td>
<input type="button" name="seven" value="7" onclick="disp(this)" />
</td>
<td>
<input type="button" name="eight" value="8" onclick="disp(this)" />
</td>
<td>
<input type="button" name="nine" value="9" onclick="disp(this)" />
</td>
<td>
<input type="button" name="left_brackets" value="(" onclick="disp(this)" />
</td>
<td>
<input type="button" name="right_brackets" value=")" onclick="disp(this)" />
</td>
</tr>
<tr>
<td>
<input type="button" name="four" value="4" onclick="disp(this)" />
</td>
<td>
<input type="button" name="five" value="5" onclick="disp(this)" />
</td>
<td>
<input type="button" name="six" value="6" onclick="disp(this)" />
</td>
<td>
<input type="button" name="multiply" value="*" onclick="disp(this)" />
</td>
<td>
<input type="button" name="divide" value="/" onclick="disp(this)" />
</td>
</tr>
<tr>
<td>
<input type="button" name="one" value="1" onclick="disp(this)" />
</td>
<td>
<input type="button" name="two" value="2" onclick="disp(this)" />
</td>
<td>
<input type="button" name="three" value="3" onclick="disp(this)" />
</td>
<td>
<input type="button" name="add" value="+" onclick="disp(this)" />
</td>
<td>
<input type="button" name="subtract" value="-" onclick="disp(this)" />
</td>
</tr>
<tr>
<td>
<input type="button" name="zero" value="0" onclick="disp(this)" />
</td>
<td>
<input type="button" name="point" value="." onclick="disp(this)" />
</td>
<td>
<input type="button" name="square" value="x²" onclick="disp(this)" />
</td>
<td>
<input type="button" name="square" value="x^y" onclick="disp(this)" />
</td>
<td>
<input type="button" name="sqrt" value="√" onclick="disp(this)" />
</td>
</tr>
</table>
</div>
</div>
<script src="javascript.js"></script>
</body>
</html>
* {
margin: 0;
padding: 0;
font-size: 16px;
outline: none;
/*消除Chrome中默认显示的input或textarea的边框*/
}
body {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
user-select: none;
/*禁止被选中*/
}
div.container {
margin: 30px;
padding: 10px;
width: 350px;
background-color: black;
}
div.screen {
margin: 0 auto;
margin-top: 5px;
width: 340px;
font-size: 0;
/*消除行内元素之间的空隙*/
}
textarea#inuptScreen {
margin: 0 auto;
padding: 5px;
width: 330px;
height: 80px;
resize: none;
font: 24px 微软雅黑;
background-color: #b3f7e0;
border: none;
overflow: hidden;
}
input#outputScreen {
margin: 0 auto;
padding: 5px;
width: 330px;
resize: none;
font: 30px 微软雅黑;
background-color: #b3f7e0;
border: none;
overflow: hidden;
border-top: 2px solid black;
}
div.input {
margin: 0 auto;
margin-top: 20px;
width: 350px;
}
div.input table,
div.input tr,
div.input td {
border: none;
border-collapse: collapse;
}
div.input input {
margin: 5px;
width: 60px;
line-height: 50px;
height: 50px;
background-color: dimgray;
border: none;
color: white;
cursor: pointer;
}
var inScn = document.getElementById("inuptScreen"),
outScn = document.getElementById("outputScreen");
inScn.value = "";
outScn.value = "";
inScn.readOnly = true; //禁用键盘输入
outScn.readOnly = true; //禁用键盘输入
//关闭窗口
function off() {
opener = null;
open('', "_self"); //浏览器兼容性,为了IE关闭时不弹出提示窗口,Chrome不需要,万恶的IE。。。
close();
}
//删除
function del() {
inScn.value = inScn.value.substring(0, inScn.value.length - 1);
}
//清屏
function clr() {
inScn.value = "";
outScn.value = "";
}
//按下等于号,显示结果
function equal() {
var reg1 = /\d/g; //匹配数字
var reg2 = /^\d+\)$/; //匹配数字+)
if (reg1.test(inScn.value)) { //如果inScn.value中有数字
var str = addZero(inScn.value); //为没加0的位置加上0,使输入的表达式更规范,便于计算
var arr = strToArr(str); //将字符串类型的表达式转换为数组
var RPN = toRPN(arr); //将中缀表达式转换为后缀表达式(RPN)
var result = calculateRPNArr(RPN); //得到计算结果
if (reg2.test(inScn.value)) { //如果输入数字+),例如:6)
outScn.value = "ERROR"; //报错,详见toRPN(arr)
} else {
if (!isNaN(result)) { //当计算结果为数字时
if (result != Infinity) { //当结果不是无穷大时,js居然把Infinity也算成number...
outScn.value = result; //显示结果
} else {
outScn.value = "ERROR"; //报错
}
} else {
outScn.value = "ERROR"; //当计算结果不是数字时,报错
}
}
} else {
if (inScn.value == "π") { //当只输入了π时
outScn.value = Math.PI; //结果显示3.1415926......
} else if (inScn.value == "") { //当inScn.value为空
outScn.value = ""; //不能显示等于号
} else {
outScn.value = "ERROR"; //如果inScn.value中无数字,则直接报错}
}
}
if (inScn.value == "" || inScn.value.indexOf("=") != -1) { //当inScn.valu为空或者已经包含一个等于号时
inScn.value += ""; //不能显示等于号
} else {
inScn.value += "="; //点击等于号,显示等于号
}
}
//将输入的显示出来
function disp(obj) {
if (outScn.value != "") {
clr(); //当计算完一次后,再输入将自动清屏
}
if (obj.value == ".") {
if (pointRule(inScn.value)) {
inScn.value += obj.value; //当这是第一个小数点或者当前位置与前面一个小数点之间有运算符时,当前小数点可以输入
} else {
inScn.value += ""; //当当前位置与前面一个小数点之间没有运算符时,小数点不能输入
}
} else if (obj.value == "x²") {
inScn.value += "^(2)"; //平方显示为^(2),便于计算
} else if (obj.value == "x^y") {
inScn.value += "^("; //幂运算显示为^(,便于计算
} else if (obj.value == "√") {
inScn.value += "√("; //开方显示为√(,便于计算
} else {
inScn.value += obj.value; //其他的点什么,显示什么
}
}
//限制小数点的输入是否合法,两个小数点之间必须要包含有运算符
function pointRule(str) {
/*
用lastIndexOf查找字符串中有没有“.”
true:如果有,把从最后一个小数点开始到字符串末尾的字符取出来,组成一个新的字符串,并且查找里面有没有运算符
true:如果有,返回true,在function disp(obj)中小数点可以输入
false:如果没有,返回false,在function disp(obj)中小数点不能输入(为空)
false:如果没有,返回true,在function disp(obj)中小数点可以输入
*/
if (str.lastIndexOf(".") != -1) { //从后往前找小数点
var str2 = str.substr(str.lastIndexOf("."), str.length);
var reg = /[\+\-\*/\^√\(\)]+/g;
if (reg.test(str2)) {
return true;
} else {
return false;
}
} else { //说明str中还没有小数点
return true;
}
}
/*
为没加0的位置加上0,使输入的表达式更规范,便于计算,例如:
当出现.5-时,变为0.5-
当出现5.+时,变为5.0+
当出现-5时,变为0-5
*/
function addZero(str) {
str = str.replace(/^\./g, "0."); //如果str以小数点开头,则变为0.
str = str.replace(/\.$/g, ".0"); //如果str以小数点结尾,则变为.0
str = str.replace(/\.(?=\D)/g, ".0"); //零宽断言,当小数点的后是非数字时,匹配非数字前面的小数点,变为0.,例如.-变为.0-,P.S.:js不支持(?<=exp),只支持(?=exp)
str = addZeroforPoint(str); //如果小数点的前面是非数字,则变为0.,例如-.5变成-0.5,由于js不支持(?<=exp),所以要自己写函数
str = str.replace(/^-/g, "0-"); //如果str以-开头,则变为0-
str = str.replace(/\(-/g, "(0-"); //如果str中出现(-,则变为(0-
return str;
}
//为前面没有数字的小数点补上0
function addZeroforPoint(str) {
var arr = new Array; //当小数点的前面是非数字时,存放这个非数字的数组
var j = 0; //为arr计数(下标)
for (var i = 0; i < str.length; i++) {
var reg = /[^0-9]\./g; //小数点的前面为非数字
var str2 = str.substr(i, 2); //在str中2个字符的取出来,从前到后
if (reg.test(str2)) { //如果str有reg的匹配文本
arr[j] = str.substr(i, 1); //将这个非数字存到arr这个字符串中
j++;
}
}
for (var n = 0; n < arr.length; n++) {
str = str.replace(arr[n] + ".", arr[n] + "0."); //当小数点的前面是非数字时,在中间加上0
}
return str;
}
//把string转换成array,string中的数字以number而不是string的形式存储在数组中
function strToArr(str) {
var numArr = new Array, //专门存放数字的临时数组
arr = new Array; //转换后的数组
var k = 0; //计数,arr的下标
// str = str.replace("π", Math.PI); //将str中的π替换成3.1415926......
str = str.replace("√", "2√"); //将str中的√替换成2√,便于计算
numArr = str.match(/(\d+)(\.\d+)?/g); //匹配出str的所有数字并存档在numArr数组中
str = str.replace(/(\d+)(\.\d+)?/g, "a"); //将str中的数字替换为单字符"a",以便用split分割
arr = str.split(""); //将str中的字符转化成数组arr
for (var i = 0; i < numArr.length; i++) { //遍历numArr数组
numArr[i] = Number(numArr[i]); //将numArr中的值从string转变成number
}
for (var j = 0; j < arr.length; j++) { //遍历arr数组
if (arr[j] == "a") { //如果a[j]的值为"a",则说明被替换过
arr[j] = numArr[k]; //将"a"换回原先的数字,不过是number类型的
k++;
}
if (arr[j] == "π") { //a[j]的值为"π"
arr[j] = Math.PI; //a[j]=3.1415926......
}
}
return arr;
}
/*
为数组添加一个priority方法:创建一个存放当前数组内优先级的数组priorityArr,将当前数组内运算符的优先级数值化,+和-为0,*和/为1,^和√为2,其他(非运算符)为-1,并将数值化的优先级存入priorityArr中,priorityArr中优先级的下标与原数组一一对应,即this[i]的优先级为priorityArr[i]
+ - * / ^ √ 非运算符
0 0 1 1 2 2 -1
*/
Array.prototype.priority = function () {
var priorityArr = new Array;
for (var i = 0; i < this.length; i++) {
switch (this[i]) {
case "+":
priorityArr[i] = 0;
break;
case "-":
priorityArr[i] = 0;
break;
case "*":
priorityArr[i] = 1;
break;
case "/":
priorityArr[i] = 1;
break;
case "^":
priorityArr[i] = 2;
break;
case "√":
priorityArr[i] = 2;
break;
default:
priorityArr[i] = -1;
}
}
return priorityArr;
}
/*
将中缀表达式转换为后缀表达式:
(1) 创建1个数组RPNArr(Reverse Polish notation),储存后缀表达式(逆波兰表示法);创建一个栈(数组)oprStack,临时存储运算符;
(2) 从左至右扫描中缀表达式(arr);
(3) 遇到操作数时,将其放入RPNArr;
(4) 遇到运算符时,比较其与oprStack栈顶运算符的优先级:
(4-1) 如果oprStack为空,或栈顶运算符为左括号“(”,或优先级比oprStack栈顶运算符的高,则直接将此运算符压入oprStack;
(4-2) 否则,将oprStack栈顶的运算符弹出并放入到RPNArr中,再次转到(4-1)与oprStack中新的栈顶运算符相比较;
(5) 遇到括号时:
(5-1) 如果是左括号“(”,则直接压入oprStack;
(5-2) 如果是右括号“)”,则依次弹出oprStack栈顶的运算符,并放入RPNArr,直到遇到左括号为止,此时将这一对括号丢弃;
(6) 重复步骤(2)至(5),直到表达式的最右边;
(7) 将oprStack中剩余的运算符依次弹出并放入RPNArr,此时RPNArr即为后缀表达式
参考http://blog.csdn.net/antineutrino/article/details/6763722
*/
//将中缀表达式转换为后缀表达式
function toRPN(arr) { //arr为待扫描数组
var RPNArr = new Array; //创建储存后缀表达式数组
var oprStack = new Array; //创建临时存储运算符的栈(数组)
for (var i = 0; i < arr.length; i++) { //扫描(遍历)arr数组
if (!isNaN(arr[i])) { //如果arr[i]为数字
RPNArr.push(arr[i]); //将arr[i]放到RPNArr中
} else if (arr[i] == "+" || arr[i] == "-" || arr[i] == "*" || arr[i] == "/" || arr[i] == "^" || arr[i] == "√") { //如果arr[i]为运算符
while (1) {
if (oprStack.length == 0 || oprStack[oprStack.length - 1] == "(" || arr.priority()[i] > oprStack.priority()[oprStack.length - 1]) { //(4-1)的情况
oprStack.push(arr[i]); //将arr[i]压入oprStack
break;
} else {
RPNArr.push(oprStack.pop()); //oprStack栈顶弹出并放入RPNArr
}
}
} else if (arr[i] == "(") { //如果arr[i]为左括号
oprStack.push(arr[i]); //将arr[i]压入oprStack
} else if (arr[i] == ")") { //如果arr[i]为右括号
while (oprStack[oprStack.length - 1] != "(" && oprStack.length > 0) { //当oprStack栈顶不为左括号时。一定要加上oprStack不为空,要不然当输入数字+)时,会出现死循环;但是这又出现一个bug,当输入数字)时,结果为数字),不是ERROR,例如:输入6),结果显示6。因此我在equal()中加入了一个判断,出现这种情况,显示ERROR。
RPNArr.push(oprStack.pop()); //oprStack栈顶弹出并放入RPNArr
}
if (oprStack[oprStack.length - 1] = "(") { //当oprStack栈顶为左括号时
oprStack.pop(); //oprStack栈顶(左括号)弹出
}
}
}
while (oprStack.length > 0) {
RPNArr.push(oprStack.pop()); //将oprStack中剩余的运算符依次弹出并放入RPNArr
}
return RPNArr;
}
/*
后缀表达式的求值:
从左至右扫描表达式RPNArr;
遇到数字时,将数字压入栈resultStack;
遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算,并将结果入resultStack;
重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。
参考http://blog.csdn.net/antineutrino/article/details/6763722
*/
//计算后缀表达式的值
function calculateRPNArr(RPNArr) { //RPNArr为存放后缀表达式的数组
var resultStack = new Array; //创建一个栈(数组)resultStack,存放计算结果
var result = 0; //计算结果
for (var i = 0; i < RPNArr.length; i++) { //扫描(遍历)RPNArr
if (!isNaN(RPNArr[i])) { //如果RPNArr[i]是数字,将RPNArr[i]压入resultStack
resultStack.push(RPNArr[i]);
} else { //如果是运算符
var y = resultStack.pop(); //弹出栈顶
var x = resultStack.pop(); //再次弹出栈顶
switch (RPNArr[i]) { //计算
case "+":
result = x + y;
break;
case "-":
result = x - y;
break;
case "*":
result = x * y;
break;
case "/":
result = x / y;
break;
case "^":
result = Math.pow(x, y);
break;
case "√":
result = Math.sqrt(y);
break;
default:
return "ERROR";
}
resultStack.push(result); //将计算结果入栈
}
}
if (resultStack.length == 1) {
return resultStack[0]; //返回最终结果,这里不要return result,因为当只输入一个数字时,return result返回的是0
} else {
return "ERROR";
}
}