本文案例代码:https://pan.baidu.com/s/1c37JFCoYpcdQd12QshI88Q 提取码:flyW
在线浏览地址
一、准备工作
这个文章的示例主要参考Windows系统只带的计算器。你可以按 win键+R 输入calc
打开计算器。
关于计算器的布局,这里就不过多的描述,你可以直接下载这个示例中的页面布局。打开后如下效果:
二、js逻辑分析
1.获取点击显示数字
完成准备工作之后,我默认你已经有和我一样的HTML页面了,这个时候,就要开始写js代码了。首先我们需要为所有按钮添加点击事件(js代码都写在js文件夹中的index.js文件)
index.js
// 获取输入框节点
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
// 获取所有按钮集合
var allDiv = document.querySelectorAll("#calculatorKey div");
// 循环按钮集合
for (let i = 0; i < allDiv.length; i++) {
// 为所有按钮添加点击事件
allDiv[i].onclick = function () {
// 获取当前按钮的文本
var text = this.innerText;
// 在控制台打印文本
console.log(text)
};
}
在上述的代码中,我通过querySelectorAll
获取了所有的按钮的集合,并遍历这个集合,为所有节点添加点击事件,为了方便我把输入框节点元素一并获取了,这个时候,如果点击按钮就可以在控制台看见打印的文本信息了。
querySelectorAll() 方法返回文档中匹配指定 CSS 选择器的所有元素,返回 NodeList 对象。
NodeList 对象表示节点的集合。可以通过索引访问,索引值从 0 开始。
我们已经获取到了文本信息,如何把它显示在计算器中呢?
其实很简单,我们只需要把文本赋值给box2
的属性innerText
即可:
box2.innerText = number
不过不急,如何的赋值我们也要去分析。我们先查看如果按下的是数字(0~9)那么计算器是如何显示的,请仔细看下方动图
我们注意到,计算器的初始值是0,当我们按下1的时候它的值就是1,当我们再按下2的时候,值是12。清空计算器,然后连续按下多个0,发现计算器的初始值没有任何变化。再清空计算器,按下2后输入多个0,值也是我们想要的结果。通过分析我们知道:
- 当计算器的输入框中为0的时候,我们输入一个值是直接重新赋值
- 当计算器的输入框中值不为0的时候,我们输入一个值是拼接的效果
实现代码如下:
for (let i = 0; i < allDiv.length; i++) {
// 为所有按钮添加点击事件
allDiv[i].onclick = function () {
var text = this.innerText;
//判断0~9之间的数
if (text >= 0 && text < 10) {
theDigital(text);
}
};
}
//如果是数字
function theDigital(number) {
// 判断当前输入框中的数字是否为0
// 如果为0直接重新赋值
// 如果不为0拼接字符串
if (box2.innerText === "0") {
box2.innerText = number;
} else {
box2.innerText += number;
}
}
2.小数点的处理
在上一小节中,我们已经能够正确的显示我们按下的数字了。在这一节中,花一点时间对小数点进行处理。
小数点的逻辑也很简单:
- 一串数字中,只允许有一个小数点。
- 如果数字中不存在小数点,则直接拼接上小数点即可
//判断0~9之间的数
if (text >= 0 && text < 10) {
theDigital(text);
} else if (text == ".") {
//判断如果按下的是小数点
//判断当前的字符串中是否存在小数点,不存则拼接
if (box2.innerText.indexOf(".") == -1) {
box2.innerText += text;
}
}
3.简单的运算
在前两节中,我们已经能在输入框中显示正确的数字。这一节,将要做一下简单的四则运算。
如何去进行四则运算呢,或者如何对数据进行存储呢?
当我们按下四则运算符的时候,就需要先对输入框中的数据进行存储,
假设我们规定了一个数组arr专门用来存储这些值,那么当我按下+
的时候,我把输入框中的1
存入到数组中,并且把+
这个运算符也存入到数组中,这个时候数组中的值就是[1,"+"]
,接下来我们输入第二个数,第二个数为2
,然后我又按了一个+
这个时候就又需要把数据存到数组中,此时为[1,"+",2,"+"]
,最后按下0.2
和=
我们就会得到这样一个数组格式:
var arr = [1, "+", 2, "+", 0.2, "="];
数组中第一个元素是数字,第二是运算符,第三个是数字,第四个是运算符,以此列推。。。
做计算的时候,我只需要: n和n+2之间通过n+1进行运算。如: 1 和 2 通过 + 进行运算结果就是1+2
看起来有点绕,但是这里必须要理解,因为后面的计算都将会用到这种格式的数组。接下来封装一个能计算这种数据的方法,如下:
index.js
//计算得数
function myCalc(arr) {
//var arr = [1, "+", 2, "+", 0.2, "="]; arr的结构
var sum = arr[0]; //总数
//遍历数组
for (var i = 1; i < arr.length - 1; i = i + 2) {
//取出运算符及其后面一个元素进行运算
var a = arr[i + 1];
var symbol = arr[i]; //符号
switch (symbol) {
case "+":
sum += a;
break;
case "-":
sum -= a;
break;
case "x":
sum *= a;
break;
case "÷":
sum /= a;
break;
}
}
return sum; //返回结果
}
这个方法的作用就是来解析这种格式的数组的。
现在假设传参就是:[1, "+", 2, "+", 0.2, "="]
我们来看一下它是如何计算的。
第4行代码把数组中第一个元素赋值给了sum
这是sum
值为1;
接下来是从下标1遍历到数组长度减1,就是[1,5)不包含5
当i为1时,第9行代码把arr[i+1]
赋值给了a
,也就是2
第10行代码又把arr[i]
赋值给了symbol
,也就是+
第11行代码进行判断,因为symbol
是+,所以执行第13行代码sum += a
,也就是1+=2,sum
为3
完成一轮循环后i+2
,i
为3
当i为3时,第9行代码把arr[i+1]
赋值给了a
,也就是0.2
第10行代码又把arr[i]
赋值给了symbol
,也就是+
第11行代码进行判断,因为symbol
是+,所以执行第13行代码sum += a
,也就是3+=0.2,sum
为3.2
完成这一轮循环后i+2
,i
为5不符合循环的条件,跳出循环,返回结果sum
值为3.2;
现在我们已经能对上述格式的数组的进行计算了,接下来继续考虑做简单的四则运算,先分析下面的这个动图:
我们可以看到,先输入26然后×9,请注意输入框中的值当我们输入×
的时候,上方的输入框的值变成了26 ×
,下方的输入框中的值仍然为26
,然后输入一个9时,上方输入框的值不变,下方输入框中的值为9,最后输入等于,上方的输入框值为26 * 9 =
,下方为234
,用代码实现这种操作呢?
- 定义一个全局变量arr,用来保存数据
- 将arr数组用空格拼接展示到上方的输入框
- 按下等于计算结果
index.js
//定义全局变量,用来保存数据,包括运算符
var arr = [];
//判断是否进行了四则运算
var ifArithmetic = false;
for (let i = 0; i < allDiv.length; i++) {
// 为所有按钮添加点击事件
allDiv[i].onclick = function () {
var text = this.innerText;
//判断0~9之间的数
if (text >= 0 && text < 10) {
...
} else if (text == ".") {}
...
} else if (text == "÷" || text == "x" || text == "-" || text == "+") {
arithmetic(text);
} else if(text == "="){
ifEqual();
}
};
}
//如果是数字
function theDigital(number) {
if (box2.innerText === "0" || ifArithmetic) {//这里发生了更改
box2.innerText = number;
ifArithmetic = false;
} else {
box2.innerText += number;
}
}
//计算得数
function calc(arr) {...}
//四则运算
function arithmetic(text) {
// 把输入框中的数和当前运算符存入到数组中
arr.push(Number(box2.innerText), text);
//对box1进行赋值
box1.innerText = arr.join(" ");
ifArithmetic = true;
}
//如果是等于
function ifEqual() {
// 把输入框中的数和当前运算符存入到数组中
arr.push(Number(box2.innerText), "=");
//对box1进行赋值
box1.innerText = arr.join(" ");
//对box2进行赋值,值为运算后的结果
box2.innerText = myCalc(arr);
}
push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
join() 方法用于把数组中的所有元素转换一个字符串。元素是通过指定的分隔符进行分隔的。
上述代码中,我们判断了如果按下的是四则运算符,则执行arithmetic
这个函数,函数中,我们先给arr数组存值(因为值为字符串,所以要先使用Number()方法转为数字),存入下方输入框的值,和运算符,然后在41行利用数组的join
方法拼接数组成字符串。并赋值到box1的innerText
属性中,这是为了能展示上方输入框的值。接着,我们把全局变量ifArithmetic
设置为true,代表这里进行了一次四则运算。因为当我们按下下一个数字时,会重新赋值,而之前对数字的处理是,输入框为0时重新赋值,不为0时拼接,这里就需要重新判断了,如25行,当ifArithmetic
为true时也需要重新赋值,并把ifArithmetic
改为false,接下来就是判断如果按了等于以后的处理。我们在第17对=
也进行了判断,执行ifEqual
这个函数,这个函数内的前两句与四则运算一致,不过多解释,box2.innerText = myCalc(arr);
则是计算数组中的值,并把结果显示在box2中,效果如下:
4.四则运算的处理
通过上一节的分析,已经能够实现基本的运算了,但是我们还有很多细节没有处理到,看下面动图:
我们发现,当按下四则运算符的时候,下方的输入框会自动计算值出来,当连续切换运算符的时候,上方的输入框的值也在变换。
下面我们就这两个问题进行解决,
1.按下运算符,下方输入框计算值。
这个简单我们只需要在arithmetic
方法中添加box2.innerText = myCalc(arr);
即可,
2.连续切换运算符时,上方的值进行变化。
//四则运算
function arithmetic(text) {
//如果前一步进行了四则运算
if (ifArithmetic) {
//改变box1里面的最后一个四则运算的值
arr[arr.length - 1] = text;
box1.innerText = arr.join(" ");
return;
}
...
}
我们需要在arithmetic
中判断,ifArithmetic
是否为true,如果是,则就是连续进行四则运算,就需要对改变数组中最后一个值,也就是最后一次的运算符。
现在四则运算基本上没有问题了,接下来处理小数点。
在之前做四则运算符的时候没有考虑到小数点的情况,导致现在如果运算后点击小数点是拼接的情况,如果你去windows的计算器中尝试你会发现,当你点击一次四则运算时,下次输入小数点的时候,输入框中的值是0.
,你也可以现在去尝试一下。
下面是这个问题的解决代码
else if (text == ".") {
//前面按了四则
if (ifArithmetic) {
box2.innerText = "0" + text;
ifArithmetic = false;
return;
}
...
}
我们只需要在判断是小数点的时候,判断一下ifArithmetic
,如果为true,则赋值成0.
,并把ifArithmetic
改为false即可。
5.’='的细节处理
我们对四则运算和小数点完成处理后,接下来,就要对等号进行处理了。请看下图:
这个图中放映了如下三种情况:
- 按下等于后,按数字0~9先初始化再赋值
- 按下等于后,按小数点也是先初始化再赋值
- 连续按下等于,连续做相同运算
第一步,先定义一个全局变量ifDy
用来判断,是否按了等于,并在ifEqual
方法中给它赋值为true
//如果是等于
function ifEqual() {
...
ifDy = true;
}
接着定义一个init
方法,用来初始化全局数据
//初始化数据
function init() {
//初始化
arr = [];
ifArithmetic = false;
ifDy = false;
box1.innerText = "";
box2.innerText = "0";
}
然后更改第一种情况,按下等于后,按数字0~9先初始化再赋值
//如果是数字
function theDigital(number) {
//如果上一次按了等于
if (ifDy) {
init();
}
if (box2.innerText === "0" || ifArithmetic) {
box2.innerText = number;
ifArithmetic = false;
} else {
box2.innerText += number;
}
}
上述的代码,这是在判断ifDy
为true的时候调用了init
方法。
第二种情况也是同样的结果方法,
else if (text == ".") {
if (ifDy) {
init();
}
//前面按了四则
if (ifArithmetic) {
box2.innerText = "0" + text;
ifArithmetic = false;
return;
}
...
}
我们最主要的是分析第三种情况,连续点击,
先对案例进行分析,
当输入22+6-3+9
然后按下等于的时候,结果如左边一张图,再按一次等于后结果为右边这张图,两个图的区别在哪里?
首先34和43之间的关系是34+9,9是从左边一张图上面一个输入框中得到的也就是arr[arr.length - 2]
,+则是arr[arr.length - 3]
,34是左边一张图下面输入的值,现在34,+,9
这三个值我们都能得到,在拿到一个等于就可以拼接成右边一张图了,代码如下:
//如果是等于
function ifEqual() {
if (ifDy) {
//把上一次的计算结果写到box1里面,加上数组的后三位数
var arr2 = [];
arr2.push(
Number(box2.innerText),
arr[arr.length - 3],
arr[arr.length - 2],
arr[arr.length - 1]
);
box1.innerText = arr2.join(" ");
box2.innerText = myCalc(arr2);
return;
}
...
}
上述代码把4个值push到了arr2中,并拼接成字符串显示到了上方输入框,下方输入框显示计算后的值。
现在以上三种情况都解决了,但是还有一种情况我在上面的动图中并没有展现出来。就是按了=
之后再按四则运算符,这里就不过多描述了,读者可以自己试下,现在上代码。
//四则运算
function arithmetic(text) {
//如果上一步是等于
if (ifDy) {
arr = [];
ifDy = false;
}
...
}
处理方式也很简单,只需要把arr
和ifDy
初始化即可。
6.剩余功能和优化
到目前为止计算器的功能基本实现了,还有两个功能也非常的简单。
else if (text == "C") {
init();
} else if (text == "CE") {
//如果是CE清除box2中的所有内容
box2.innerText = 0;
if (ifDy) {
init();
}
}
如果是C
直接初始化,如果是CE
情况下方输入框的值,要是上一次按了=
则同样清空。
浮点数计算的处理!!!
关于浮点数的处理,可以直接去看这个博客JS浮点数计算误差解释及解决方案
这里直接引用解决方案
//浮点数加
function Add(arg1, arg2) {
var r1, r2, m;
try {
r1 = arg1.toString().split(".")[1].length
} catch (e) {
r1 = 0
}
try {
r2 = arg2.toString().split(".")[1].length
} catch (e) {
r2 = 0
}
m = Math.pow(10, Math.max(r1, r2));
return (arg1 * m + arg2 * m) / m;
}
//浮点数减
function Sub(arg1, arg2) {
var r1, r2, m, n;
try {
r1 = arg1.toString().split(".")[1].length
} catch (e) {
r1 = 0
}
try {
r2 = arg2.toString().split(".")[1].length
} catch (e) {
r2 = 0
}
m = Math.pow(10, Math.max(r1, r2));
//动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
//浮点数乘
function Mul(arg1, arg2) {
var m = 0,
s1 = arg1.toString(),
s2 = arg2.toString();
try {
m += s1.split(".")[1].length
} catch (e) {}
try {
m += s2.split(".")[1].length
} catch (e) {}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
//浮点数除
function Div(arg1, arg2) {
var t1 = 0,
t2 = 0,
r1, r2;
try {
t1 = arg1.toString().split(".")[1].length
} catch (e) {}
try {
t2 = arg2.toString().split(".")[1].length
} catch (e) {}
r1 = Number(arg1.toString().replace(".", ""));
r2 = Number(arg2.toString().replace(".", ""));
return (r1 / r2) * Math.pow(10, t2 - t1);
}
然后改造myCalc
方法
//计算得数
function myCalc(arr) {
var sum = arr[0]; //总数
//遍历数组
for (var i = 1; i < arr.length - 1; i = i + 2) {
//取出运算符及其后面一个元素进行运算
var a = arr[i + 1];
var symbol = arr[i]; //符号
switch (symbol) {
case "+":
sum = Add(sum, a);
break;
case "-":
sum = Sub(sum, a);
break;
case "x":
sum = Mul(sum, a);
break;
case "÷":
sum = Div(sum, a);
break;
}
}
return sum; //返回结果
}
总结
本片文章注意介绍了简易计算器的实现过程,代码都很简单,需要注意的就是分析,在这个计算器中还有很多功能和细节没有去处理,比如,除数不能为0。这个需要大家自己去完善。