在 HTML 中使用 JavaScript 构建计算器的不同方法

2025年3月17日 | 阅读 14 分钟

学习 JavaScript 时,构建计算器是最初的挑战之一。我们可以采用不同的方法来创建不同类型的计算器。我们将创建一个简单的计算器,包含四个主要运算符 +、-、* 和 /,以及括号,并且能够处理小数。本教程涵盖创建简单计算器的三种不同方法:

  1. 使用 eval() 方法
  2. 使用 for 循环
  3. 使用移位位算法

HTML、CSS 和 JS 简介

  1. HTML 是用于构建网页结构的标记语言。
  2. CSS 是用于设计网页元素布局和外观的样式语言。
  3. JS 是用于定义网页元素行为和功能的脚本语言。

网站上已经有关于这三种方法的单独教程。这是一个包含这三种相关方法的合集。使用这三种方法,JavaScript 代码会有所不同。我们将为所有三种方法使用相同的 HTML 和 CSS 代码。

HTML

思路是创建一个表格。表格的第一行应该是显示屏。接下来的几行必须包含数字按钮、清除、退格、等于按钮和运算符按钮。

  1. 点击数字或运算符时,按钮的值必须显示在显示屏上。
  2. 点击 C 时,显示屏必须清空。
  3. 点击 B 时,显示屏上的最后一个字符必须被删除。
  4. 点击 = 时,显示屏上的所有内容都将使用 eval() 进行计算,然后显示屏将显示输入表达式的结果。

因此,我们需要在 JavaScript 中为四种类型的按钮编写四个函数。您可以使用 CSS 以任何您想要的方式为计算器设置样式。为简单起见,我们没有使用很多样式功能。

  1. 创建一个带有 .html 扩展名的新文件: calci.html
  2. 在 head 部分,我们将标题设置为 calci。body 中的第一个标签是 center,用于将标题和计算器放在网页中央。
  3. 然后,我们将网页的主标题设置为“简单计算器”。
  4. 我们构建了一个表格,如下所示:
    1. 每一行都包含要按下的按钮,并且每次点击按钮时,都会从 js 调用一个函数进行计算。
    2. 第一行的按钮:0、(、)、空列、C、B。C 用于清除显示屏,B 用于退格。
    3. 第二行的按钮:7、8、9、空列、+ 和 -。
    4. 第三行的按钮:6、5、4、空列、* 和 /。
    5. 第四行的按钮:1、2、3、空列、小数点和 =。
  5. 我们将使用内联 CSS 和 JS,在 head 部分使用 <style></style> 和 <script></script> 标签。

这是 HTML 代码

输出

Different ways to build a calculator in HTML using JavaScript
  • 按下这些按钮时,什么也不会发生,因为我们还没有添加任何 JavaScript 代码。

现在,是时候使用 CSS 设计网页了。

CSS

输出

Different ways to build a calculator in HTML using JavaScript

以下是所有使用的 CSS 属性及其解释的表格:

表格样式

属性说明
table_layout: fixed表格中所有单元格的布局保持固定(列宽),无论其中内容的长度如何。
border-spacing: 10px单元格边框之间的距离或间距。仅当 border-collapse 设置为 separate 时才适用。我们可以设置垂直和水平距离。
box-shadow为元素添加阴影。通过逗号分隔,我们可以为单个元素添加多个阴影。
border-radius使元素的角落边缘变圆。(为元素添加圆角)。值越大,圆角越圆。我们可以为四个角指定四个值。
background-color为元素的背景添加颜色。我们可以使用颜色名称或十六进制代码。
border一个用于为元素的边框添加宽度、样式和颜色的属性。

Button (按钮)

属性说明
padding在元素与其周围边框之间创建空间。使用 padding-top、padding-right、padding-bottom 和 padding-left,我们可以为元素的每个侧面设置间距。
border-radius使元素的角落边缘变圆。(为元素添加圆角)。值越大,圆角越圆。我们可以为四个角指定四个值。
hover: background-color使用 element:hover,我们可以为用户将光标悬停(移动)到元素上时添加一个属性。在这里,我们正在更改背景颜色。
color字体颜色。
active: tranform使用 element:active,我们可以为用户点击元素时为其添加一个属性。Transform 用于为元素添加 2D 效果。在这里,我们使用 scale()。此函数可以调整元素大小,然后恢复到原始大小。我们用它来获得按下时的按钮效果。
body: hover h1{}当用户悬停在 body 上时,我们可以使用此语法更改 h1 的属性。

现在,主要功能在于 JavaScript,我们将使用上面提到的三种方法来开发它。

1. eval() 方法

eval() 函数接受一个字符串形式的表达式作为输入,对其进行评估并返回结果。

例如

eval("4*3+2") 返回 14

但是,不建议使用 eval(),因为它可能导致恶意攻击。如上所述,eval() 可以接受任何形式的表达式,甚至是长字符串。如果有人——黑客或某个恶作剧用户——输入一个无限值,它会一直执行,减慢整个服务器的评估速度,而不是识别攻击。

  • 使用 eval() 可能存在安全隐患。如果程序接受用户输入并将其传递给 eval(),黑客就可以输入恶意代码作为输入,该代码可以在函数的作用域内任意运行,泄露机密凭证,如登录详细信息等。
  • 我们需要对 eval() 函数的输入进行清理,或者最好使用其他函数代替 eval()。
    1. 在获取用户输入后,我们将表达式作为参数传递给函数。
    2. 观察 HTML 代码,我们调用了四个函数 fun()、C()、B() 和 res()。
    3. fun(a)
      1. 点击所有数字和运算符按钮时,我们调用 fun() 并将数字或运算符的值作为参数传递。
      2. 在函数中,我们需要将该值打印到显示输入框中,因此,我们将参数连接到输入框中已有的值。
    4. C(): 我们只需将显示输入框的值重新赋给一个空字符串即可清除显示。
    5. B(): 我们将获取显示屏上的整个字符串,对其进行切片,删除最后一个字符,然后将其重新赋给显示屏。
    6. res(): 此函数在点击 = 时激活。我们只需获取显示屏上的整个表达式,将其作为参数传递给 eval(),然后将结果重新赋给显示屏。

内联 JS 代码

2. 使用 for 循环

在此方法中,我们使用四个循环来处理四个主要运算符。四个循环按 BODMAS 的顺序排列,以遵循评估顺序——除法、乘法、加法和减法。我们将使用两个数组——一个包含运算符的索引,一个包含表达式中的操作数。

所有功能都集中在 res() 中。首先,观察代码。

输出

Different ways to build a calculator in HTML using JavaScript

机制

  1. 显示屏上输入的表达式存储在变量a中。
  2. 现在,我们创建了两个数组index[]numbers[]
  3. 使用 for 循环,我们遍历表达式,将运算符的索引存储到数组 index 中,并将它们打印到 HTML 中定义的段落中。
  4. 所以,数组 index[] 包含 a 中运算符的索引。
  5. 现在,我们需要使用运算符将字符串形式的表达式分隔成数字。
  6. 我们创建另一个数组res[]
  7. 让我们举个例子来更好地理解。
    a = 8 + 9 - 8 * 23
  8. 现在,数组index[] = [1, 3, 5]
  9. 假设表达式以 - 开头,为了处理这种情况,我们检查 a[0] 是否为 -,如果是负号,则在表达式开头添加一个 0。
    例如:-8-9 -> 0-8-9
  10. 在 for 循环中,我们遍历表达式,使用 index[] 将 a 中的值或数字填充到 res[] 中。
  11. 获取字符串后,我们使用 parseFloat() 将值转换为浮点数。我们将其打印到下一个段落中。
  12. 现在,根据上面的例子
    a = 8 + 9 - 8 * 23
    index = [1, 3, 5]
    res = [8, 9, 8, 23]
  13. 请注意,res 的大小 = index 的大小 - 1
  14. 我们需要遵循数学规则 BODMAS 来计算任何表达式。
  15. 如果有像 3-2*5 这样的表达式
    答案应该是 3-10 = -7,而不是 1*5 = 5。乘法优先于减法。
  16. BODMAS:括号、of、除法、乘法、加法和减法。
  17. 此计算器中没有括号,所以我们只需要遵循四个运算符(算术 - +、-、* 和 /)。
  18. 在这里,我们使用了四个 for 循环。
  19. 思路是遍历 index[] 并对 res[j] 和 res[j+1] 执行 index[i] 处的运算符操作。要遍历 index[],我们使用 i 和 j 来遍历 res[]。
  20. splice() 用于在计算完操作后操作 res[]。它将所有值拉到空索引中,每个空索引都得到一个值。
  21. 第一个 for 循环执行所有除法,第二个执行乘法,第三个执行加法,最后一个执行减法。
  22. 在上面的例子中

a = 8 + 9 - 8 * 23

在第二个循环中

index = [1, 3, 5]

res = [8, 9, 8, 23]

a[index[2]] = *

res[1] = res[2]*res[3] = 8*23 = 184

res = [8, 9, 184]

在第三个循环中

index = [1, 3, 5]

res = [8, 9, 184]

a[index[0]] = +

res[0] = res[0] + res[1] = 8 + 9 = 17

res = [17, 184]

在第四个循环中:

index = [1, 3, 5]

res = [17, 184]

a[res[1]] = 17 - 184 = -167

res = [-167]

  1. 最后,我们将显示屏上的表达式替换为结果。

局限性

  1. 此代码无法处理 ()、连续运算符,并且由于使用了过多的循环而效率不高。这只是我们制作计算器的一个想法。
  2. 我们可以扩展代码来处理这些。请注意,在我们举的例子中,表达式中没有 /,但第一个循环只是被遍历而没有任何作用。因此,这不是一个非常有效的方法。
  3. 我们将在下一个教程“Dijkstra 的移位位算法”中介绍最佳方法。

3. 使用移位位算法

通常,此算法用于将中缀表达式转换为后缀表达式,也称为逆波兰表示法(RPN)。a + b 格式的表达式是中缀表示法,而 ab+ 格式是其后缀表示法。评估后缀表达式很容易,通常,计算机将任何给定的中缀表达式转换为后缀表示法来使用栈计算答案。

与其一次性输出所有代码,不如将其分解。首先是显示部分。

  1. fun(a)
    1. 点击所有数字和运算符按钮时,我们调用 fun() 并将数字或运算符的值作为参数传递。
    2. 在函数中,我们需要将该值打印到显示输入框中,因此,我们将参数连接到输入框中已有的值。
  2. C(): 我们只需将显示输入框的值重新赋给一个空字符串即可清除显示。
  3. B(): 我们将获取显示屏上的整个字符串,对其进行切片,删除最后一个字符,然后将其重新赋给显示屏。
  4. res(): 此函数在点击 = 时激活。

使用 trim(),我们去除了任何多余的空格,然后将表达式作为输入传递给另一个函数 fun1()。

fun1()

此函数在获取表达式作为输入后包含计算器的实际功能或后端。

代码

说明

  1. 我们声明了两个数组(栈),values[] 和 operators[]。
  2. opc 和 clc 分别代表左括号和右括号。
  3. 在下一个 for 循环中,我们检查左括号的数量是否等于右括号的数量。否则,它会返回表达式无效。
  4. 根据 BODMAS,我们首先需要计算括号内的子表达式,因此,我们检查是否有任何括号,如果有,我们就提取括号之间的子表达式,并使用子表达式作为输入再次调用 fun1()。
  5. 如果表达式中有括号,此递归将继续。
  6. 现在,来到实际计算部分。
    1. 我们检查表达式前面是否有符号,如果有,我们就将 0 推入 values。假设,如果表达式是 -8:0-8 仍然是 -8。
    2. 然后,我们检查是否有连续的运算符,这会再次返回无效表达式。
    3. 如果存在操作数,则将其推入 values 栈。
    4. 现在,如果存在运算符。
      1. 如果 operators 栈为空,则将运算符推入栈。
      2. 如果找到的运算符的优先级大于 operators 栈顶运算符的优先级,则将运算符推入栈。
      3. 如果找到的运算符的优先级小于或等于 operators 栈顶运算符的优先级,则从 operators 栈弹出最后一个运算符和从 values 栈弹出两个操作数,对这两个操作数执行运算,并将结果推入 values 栈。
      4. 继续此过程,直到找到的运算符的优先级大于栈顶运算符的优先级。
  7. 现在,我们处理了 + 和 - 可以连续出现的情况。这通常发生在有括号时:3 + (2 - 3) -> 3 + - 1 -> 2。
  8. 在这种情况下,我们获取带负号的操作数,将其转换为浮点数并将其作为常规操作数推入 values 栈。
  9. 最后,如果 operators 栈不为空,这意味着仍有一些运算符和操作数剩余,则从 values 栈弹出操作数,并应用 operators 栈顶的运算符,然后从 operators 栈弹出运算符。
  10. 继续此过程,直到 operators 和 values 栈中没有剩余的运算符和操作数。
  11. precedence 函数用于管理 BODMAS/PEDMAS 中的优先级规则。

示例

表达 3 + (5 + 9 * 9)

  1. 括号内的表达式再次传递给函数。
    • 5 被推入 values 栈。
    • + 被推入 operators 栈。
    • 9 被推入 values 栈。
    • (的优先级大于+,因此它被推入 values 栈。
    • 9 被推入 values 栈。

      Different ways to build a calculator in HTML using JavaScript
      运算符
    • 从 values 栈弹出两个操作数:9 和 9,并应用 operators 栈顶的运算符:9 * 9 = 81。将 81 推入 values 栈。
      Different ways to build a calculator in HTML using JavaScript
    • 81 + 5 = 86.
  2. 现在,86 被连接到括号内表达式的原始字符串中。
  3. 表达式:3 + 86
  4. 3 被推入 values 栈。
  5. + 被推入 operators 栈。
  6. 86 被推入 values 栈。
    Different ways to build a calculator in HTML using JavaScript
  7. 3 + 86 = 89 是最终结果

输出屏幕

Different ways to build a calculator in HTML using JavaScript
Different ways to build a calculator in HTML using JavaScript