这个史诗般的教程通过描述如何使用该语言开发一个简单的计算器应用程序,为 JavaScript 新手提供了一个可靠的练习。

面向初学者的 javascript 项目(8 部分系列)

  • 1 构建你的第一个 JavaScript 应用程序——一个随机报价生成器
  • 2 如何使用 JavaScript 构建维基百科搜索应用程序
  • 3 如何使用 JavaScript 构建计算器应用程序
  • 4 如何使用 JavaScript 构建待办事项列表应用程序
  • 5 如何使用 JavaScript 构建自定义 HTML5 视频播放器
  • 6 如何使用 JavaScript 构建 Simon 游戏
  • 7 如何使用 JavaScript 构建番茄钟计时器应用程序
  • 8 如何创建您的第一个 Chrome 扩展程序

我们将在本教程中开发的计算器应用程序非常简单。它或多或少与杂货店中的那些计算器类似。应用程序的布局是用CSS 网格制作的。如果您想了解它是如何实现的,请务必查看本教程,因为本教程仅关注用于使计算器工作的 JavaScript 逻辑。

这是已完成项目的现场演示。在潜入之前,您可以使用它来感受一下您将要构建的内容。

先决条件

本教程假设您具有 JavaScript 的基本知识。我将尽可能分解每个步骤,因此即使您之前没有在浏览器中构建应用程序的经验,也应该很容易理解。

在你开始之前

你可以找到起点 本教程上的jsfiddle。它包含用于构建计算器布局的所有必要标记和样式。标记几乎与上一教程的最终状态相同,在该教程中我讨论了如何制作计算器布局,但我做了一些小改动,因此请务必使用这个。

首先将代码分叉到一个新的小提琴上,然后输入每一步直到最后。如果您愿意,可以在其他在线代码游乐场或本地机器上随意完成本教程。

入门

通过使用输入按钮构建有效表达式,任何人都应该能够在我们的计算器应用程序上执行四种最常见的算术运算(加法、减法、乘法和除法),并将结果显示在屏幕上。有效表达式的示例如下所示:

12 + 10

要构造一个有效的算术表达式,我们需要跟踪一些事情:第一个操作数 ( 12)、运算符 ( +) 和第二个操作数 ( 10)。

让我们首先创建一个对象来帮助我们跟踪这些值。在 JSFiddle 的 JavaScript 窗格顶部添加以下代码:

const calculator = {displayValue: '0',firstOperand: null,waitingForSecondOperand: false,operator: null,
};

calculator上面的对象包含我们构建有效表达式所需的一切:

  • displayValue保存表示用户输入或操作结果的字符串值。这就是我们如何跟踪应该在屏幕上显示的内容。
  • firstOperand将存储任何表达式的第一个操作数。暂时就这样 null了。
  • operator键将存储在运营商的表达式。它的初始值也是null
  • waitingForSecondOperand本质上用作检查第一个操作数和运算符是否都已输入的方法。如果是true,则用户输入的下一个数字将构成第二个操作数。

更新显示

目前,计算器屏幕是空白的。我们需要displayValue始终在屏幕上显示属性的内容 。我们将为此创建一个函数,以便任何时候在应用程序中执行操作时,我们都可以调用它以使用 displayValue.

继续并在calculator对象下方键入以下内容:

function updateDisplay() {// select the element with class of `calculator-screen`const display = document.querySelector('.calculator-screen');// update the value of the element with the contents of `displayValue`display.value = calculator.displayValue;
}updateDisplay();

如果您查看应用程序的HTML代码,您会发现“屏幕”实际上只是一个禁用的文本输入:

<input type="text" class="calculator-screen" value="" disabled />

我们不能直接用键盘输入,但我们可以用 JavaScript 改变它的值。这就是updateDisplay函数所做的。现在您应该看到计算器屏幕上显示零。

喘口气, 在这一步结束时查看 完整的代码。

处理按键

计算器上有四组键:数字(0-9)、运算符(+、-、⨉、÷、=)、小数点(.)和复位键(AC)。在本节中,我们将监听对计算器键的点击,并确定点击的是什么类型的键。

在 JavaScript 选项卡底部添加此代码片段:

const keys = document.querySelector('.calculator-keys');
keys.addEventListener('click', (event) => {// Access the clicked elementconst { target } = event;// Check if the clicked element is a button.// If not, exit from the functionif (!target.matches('button')) {return;}if (target.classList.contains('operator')) {console.log('operator', target.value);return;}if (target.classList.contains('decimal')) {console.log('decimal', target.value);return;}if (target.classList.contains('all-clear')) {console.log('clear', target.value);return;}console.log('digit', target.value);
});

在上面的代码片段中,我们正在侦听类为click的元素上的事件calculator-keys。由于计算器上的所有键都是该元素的子元素,因此click事件也会过滤到它们。这称为 事件委托

在事件侦听器的回调函数中,我们target 使用解构赋值 来提取单击事件的属性,这使得将对象属性解包为不同的变量变得容易。

const { target } = event;
// is equivalent to
const target = event.target;

target变量是表示被点击的元件的对象。如果此元素不是按钮(例如,如果您单击按钮之间的空格),我们将通过提前返回来退出该功能。否则,单击的按钮类型及其值将记录到控制台。

在继续下一步之前,请务必尝试一下。打开浏览器控制台并单击任何按钮。键的类型和值应相应地记录到控制台。

检测到正确类型的密钥并将其记录到控制台

喘口气, 在这一步结束时查看 完整的代码。

输入数字

在这一步中,我们将使数字按钮起作用,以便在单击它们中的任何一个时,屏幕上都会显示相应的值。

由于对象的displayValue属性calculator代表用户的输入,所以我们需要在点击任何数字时对其进行修改。创建一个inputDigitcalculator对象下面调用的新函数:

function inputDigit(digit) {const { displayValue } = calculator;// Overwrite `displayValue` if the current value is '0' otherwise append to itcalculator.displayValue = displayValue === '0' ? digit : displayValue + digit;
}

接下来,替换click事件侦听器回调函数中的以下行:

console.log('digit', target.value);

使用以下代码:

inputDigit(target.value);
updateDisplay();

inputDigit函数中,三元运算符 ( ?) 用于检查计算器上显示的当前值是否为零。如果是,calculator.displayValue则被单击的任何数字覆盖。否则,如果displayValue是非零数字,则通过字符串连接将数字附加到其上。

最后,updateDisplay()调用该函数,以便在displayValue单击每个按钮后将属性的新内容反映在屏幕上。通过单击任何数字按钮进行尝试。显示应更新为您单击的任何数字。

喘口气, 在这一步结束时查看 完整的代码。

输入小数点

当点击小数点键时,我们需要在屏幕上显示的任何内容后附加一个小数点,除非它已经包含小数点。

这是我们如何实现这一目标。创建一个名为inputDecimal 下面的新函数inputDigit

function inputDecimal(dot) {// If the `displayValue` property does not contain a decimal pointif (!calculator.displayValue.includes(dot)) {// Append the decimal pointcalculator.displayValue += dot;}
}

在该inputDecimal函数中, includes() 方法用于检查是否displayValue尚未包含小数点。如果是,则会在数字后附加一个点。否则,函数退出。

之后,替换键的事件侦听器回调函数中的以下行:

console.log('decimal', target.value);

使用以下代码行:

inputDecimal(target.value);
updateDisplay();

此时,您应该可以成功添加输入小数点并显示在屏幕上。

喘口气, 在这一步结束时查看 完整的代码。

处理操作员

下一步是让计算器上的运算符(+、-、⨉、÷、=)工作。需要考虑三种情况:

1.当用户输入第一个操作数后点击操作符时

此时, 的内容displayValue应该存储在firstOperand属性下, 并且operator应该使用单击的任何运算符更新属性。

创建一个名为handleOperator下面的新函数inputDecimal

function handleOperator(nextOperator) {// Destructure the properties on the calculator objectconst { firstOperand, displayValue, operator } = calculator// `parseFloat` converts the string contents of `displayValue`// to a floating-point numberconst inputValue = parseFloat(displayValue);// verify that `firstOperand` is null and that the `inputValue`// is not a `NaN` valueif (firstOperand === null && !isNaN(inputValue)) {// Update the firstOperand propertycalculator.firstOperand = inputValue;}calculator.waitingForSecondOperand = true;calculator.operator = nextOperator;
}

当按下操作符键时, 的内容displayValue被转换为浮点数(即带小数点的数字)并将结果存储在firstOperand属性中。

operator属性还设置为单击任何操作符键,而 waitingForSecondOperand设置为true表示已输入第一个操作数,用户接下来输入的任何数字将构成第二个操作数。

此时,查看calculator每次按下按钮时对象的属性如何更新是很有用的。将以下行添加到inputDigithandleOperator函数的末尾:

console.log(calculator);

然后在键的click事件侦听器回调函数中替换以下行:

console.log('operator', target.value);

使用以下代码:

handleOperator(target.value);
updateDisplay();

此时,依次单击以下键,尝试构造有效的算术运算:12 +。请注意,当+按下该键时,firstOperandoperator属性的值分别更新为12和 ,+ 而waitingForSecondOperand设置为 true 表示计算器现在正在等待输入第二个操作数。

如果您尝试输入第二个操作数,您会注意到它被附加到第一个操作数而不是覆盖它。

让我们通过更新inputDigit函数来解决这个问题,如下所示:

function inputDigit(digit) {const { displayValue, waitingForSecondOperand } = calculator;if (waitingForSecondOperand === true) {calculator.displayValue = digit;calculator.waitingForSecondOperand = false;} else {calculator.displayValue = displayValue === '0' ? digit : displayValue + digit;}console.log(calculator);
}

如果该waitingForSecondOperand属性设置为true,则该displayValue 属性将被单击的数字覆盖。否则,将执行与之前相同的检查,displayValue酌情覆盖或附加。

喘口气, 在这一步结束时查看 完整的代码。

2.当用户输入第二个操作数后点击操作符时

我们需要处理的第二种情况发生在用户输入第二个操作数并单击操作键时。此时,评估表达式的所有成分都存在,因此我们需要这样做并将结果显示在屏幕上。该firstOperand也需要更新,以便结果能在接下来的计算中重复使用。

创建一个calculate如下handleOperator所示的新函数:

function calculate(firstOperand, secondOperand, operator) {if (operator === '+') {return firstOperand + secondOperand;} else if (operator === '-') {return firstOperand - secondOperand;} else if (operator === '*') {return firstOperand * secondOperand;} else if (operator === '/') {return firstOperand / secondOperand;}return secondOperand;
}

此函数将第一个操作数、第二个操作数和运算符作为参数,并检查运算符的值以确定应如何计算表达式。如果运算符是=,则第二个操作数将按原样返回。

接下来,更新handleOperator函数,如下所示:

function handleOperator(nextOperator) {const { firstOperand, displayValue, operator } = calculatorconst inputValue = parseFloat(displayValue);if (firstOperand == null && !isNaN(inputValue)) {calculator.firstOperand = inputValue;} else if (operator) {const result = calculate(firstOperand, inputValue, operator);calculator.displayValue = String(result);calculator.firstOperand = result;}calculator.waitingForSecondOperand = true;calculator.operator = nextOperator;console.log(calculator);
}

else if添加的块handleOperator检查operator属性是否已分配给运算符。如果是,calculate则调用该函数并将结果保存在result变量中。

该结果随后通过更新displayValue 属性显示给用户。此外, 的值firstOperand被更新为结果,以便它可以在下一个计算器中使用。

试试看。输入12 + 10 =计算器并注意屏幕上显示正确的结果。当您链接一系列操作时,它也适用。所以5 * 20 - 14 =应该给86作为结果。

这是因为打减号键触发第一动作的计算(5 * 20),其结果(100)随后被设定为 firstOperand以时间为下一次计算,所以我们进入14第二操作数和命中=键,calculate执行功能再次得到86作为结果也被设置为firstOperand下一个操作,依此类推。

喘口气, 在这一步结束时查看 完整的代码。

3. 连续输入两个或多个操作符时

改变一个人想要执行的操作类型的想法是很常见的,因此计算器必须正确处理它。

假设您想将 7 和 2 相加,您将单击7 + 2 =这将产生正确的结果。但是让我们假设在点击 之后7 +,您改变主意并决定从 7 中减去 2。而不是清除计算器并重新开始,您应该能够点击-覆盖 +之前输入的 。

请记住,在输入运算符时,waitingForSecondOperand 将被设置为 ,true因为计算器期望在运算符键之后输入第二个操作数。我们可以使用这种特性来更新操作符键并防止任何计算,直到输入第二个操作数。

handleOperator函数修改为如下所示:

function handleOperator(nextOperator) {const { firstOperand, displayValue, operator } = calculatorconst inputValue = parseFloat(displayValue);if (operator && calculator.waitingForSecondOperand)  {calculator.operator = nextOperator;console.log(calculator);return;}if (firstOperand == null && !isNaN(inputValue)) {calculator.firstOperand = inputValue;} else if (operator) {const result = calculate(firstOperand, inputValue, operator);calculator.displayValue = String(result);calculator.firstOperand = result;}calculator.waitingForSecondOperand = true;calculator.operator = nextOperator;console.log(calculator);
}

相关变化在上面突出显示。该if语句检查 a 是否 operator已经存在,并且 ifwaitingForSecondOperand设置为true。如果是,则operator属性的值将替换为 new 运算符并且函数退出,以便不执行任何计算。

试试看。输入一些数字后单击多个运算符并在控制台中监视计算器对象。请注意,该operator属性每次都会更新,并且在您提供第二个操作数之前不会执行任何计算。

喘口气, 在这一步结束时查看 完整的代码。

重置计算器

最后的任务是确保用户可以通过按键将计算器重置为其初始状态。在大多数计算器中,该AC按钮用于将计算器重置为默认状态,这就是我们在这里要做的。

继续在下面创建一个新函数,如下calculate所示:

function resetCalculator() {calculator.displayValue = '0';calculator.firstOperand = null;calculator.waitingForSecondOperand = false;calculator.operator = null;console.log(calculator);
}

然后在键的事件侦听器回调函数中替换以下行:

console.log('all clear', target.value)

使用以下代码:

resetCalculator();
updateDisplay();

resetCalculator函数将calculator 对象的所有属性设置为其原始值。单击AC计算器上的键现在应该可以正常工作了。您可以calculator在控制台中查看对象进行确认。

喘口气, 在这一步结束时查看 完整的代码。

修复小数错误

如果在单击运算符后输入小数点,它会附加到第一个操作数而不是第二个操作数的一部分。

我们可以通过对inputDecimal 函数进行以下修改来修复此错误:

function inputDecimal(dot) {if (calculator.waitingForSecondOperand === true) {calculator.displayValue = '0.'calculator.waitingForSecondOperand = false;return}if (!calculator.displayValue.includes(dot)) {calculator.displayValue += dot;}
}

IfwaitingForSecondOperand设置为true并输入小数点, displayValue变为0.waitingForSecondOperand设置为 false 以便任何其他数字作为第二个操作数的一部分附加。

该错误现已修复

喘口气, 在这一步结束时查看 完整的代码。

重构事件监听器

更新键的事件侦听器,如下所示。所有if块都已被替换为单个switch块,并且updateDisplay()仅在函数结束时调用一次。

keys.addEventListener('click', event => {const { target } = event;const { value } = target;if (!target.matches('button')) {return;}switch (value) {case '+':case '-':case '*':case '/':case '=':handleOperator(value);break;case '.':inputDecimal(value);break;case 'all-clear':resetCalculator();break;default:// check if the key is an integerif (Number.isInteger(parseFloat(value))) {inputDigit(value);}}updateDisplay();
});

这样,向计算器添加新函数就容易多了,而且您不再需要updateDisplay()在每次操作后调用该函数。

喘口气, 在这一步结束时查看 完整的代码。

浮点精度

我想提请您注意当运算结果为浮点数时出现的问题。例如,0.1 + 0.2产生 0.30000000000000004而不是0.3您可能期望的那样。

在其他情况下,您会得到预期的结果。例如0.1 + 0.4产量0.5。可以在此处找到有关为什么会发生这种情况的详细说明 。请务必阅读它。

上述链接页面中建议的解决此问题的潜在解决方案之一是将结果格式化为固定的小数位数,以便丢弃其他小数位。我们可以将 JavaScript parseFloat 函数与 Number.toFixed方法结合起来, 在我们的计算器应用中实现这个解决方案。

handleOperator函数中,替换以下行:

calculator.displayValue = String(result);

使用以下代码:

calculator.displayValue = `${parseFloat(result.toFixed(7))}`;

toFixed()方法接受 0 到 20 之间的值,并确保小数点后的位数限制为该值。如有必要,返回值可以四舍五入或用零填充。

在前面的例子中,0.1 + 0.2yield 0.30000000000000004toFixed(7)在结果上使用 会将小数点后的数字限制为七位:

0.30000000000000004.toFixed(7) // 0.3000000

那些额外的零并不重要,所以我们可以用它parseFloat来摆脱它们:

parseFloat(0.30000000000000004.toFixed(7)) // 0.3

这就是我们如何能够在我们的应用程序中解决这个问题。在这种情况下,我选择了数字 7,因为它对于这个计算器应用程序来说已经足够好了。在其他情况下可能需要更高的精度。

作为额外的奖励,结果现在适合屏幕

喘口气, 在这一步结束时查看 完整的代码。

奖金内容

我已经为我的 Patreon 支持者准备了一些对这个计算器应用程序的进一步增强。应用程序中添加了以下函数:sin、cos、tan、log、square、square root、factorial、percent、plus/minus、pi 和 ce。

不要忘记在GitHub - Freshman-tech/javascript-calculator: Build a simple calculator app with JavaScriptBuild a simple calculator app with JavaScript. Contribute to Freshman-tech/javascript-calculator development by creating an account on GitHub.https://github.com/Freshman-tech/javascript-calculator查看完整的源代码。

结论

我的教程到此结束,希望你能从中学到很多。如果文章的某个部分对您来说不够清楚,请随时发表评论,并 订阅我的时事通讯以在您的收件箱中获得更多像这样的精彩教程。

如何使用 JavaScript 构建计算器应用程序相关推荐

  1. javascript程序_使用JavaScript构建实时应用程序的5种方法

    javascript程序 There was a point in time where we didn't expect too much from web pages. Which reminds ...

  2. 如何使用.NET Core,SQL和HTML和JavaScript构建CRUD应用程序

    目录 介绍 使用代码 下载源代码696.3 KB 介绍 在本文中,我将说明一个端到端解决方案(服务端>后端>前端),以创建新的.NET Core Web服务并在前端使用该服务.我将逐步演示 ...

  3. electron 桌面程序_如何使用Electron使用JavaScript构建您的第一个桌面应用程序

    electron 桌面程序 by Carol-Theodor Pelu 通过Carol-Theodor Pelu 如何使用Electron使用JavaScript构建您的第一个桌面应用程序 (How ...

  4. html getelementbyid 修改图片_如何使用HTML、CSS和JS轻松构建桌面应用程序

    HTML,CSS和JavaScript语言是否可以真正用于构建桌面应用程序? 答案是肯定的. 在这篇文章中,我们将主要关注Electron如何使用HTML.CSS和Javascript等网络技术来创建 ...

  5. idea自动构建web项目_构建Web应用程序以自动执行系统管理员任务

    idea自动构建web项目 系统管理员(sysadmin)每年在重复性任务上浪费数千小时. 幸运的是,使用开源工具构建的Web应用程序可以自动消除很大一部分痛苦. 例如,使用Python和JavaSc ...

  6. 如何构建Pebble应用程序

    您将要创造的 Pebble智能手表自2012年非常成功的Kickstarter活动以来就一直存在,并且是智能手表用户中的热门选择. 这是由于其出色的电池寿命,低廉的价格,多种型号可供选择以及与iOS和 ...

  7. 使用 Node.js、Express、AngularJS 和 MongoDB 构建一个Web程序

    为什么80%的码农都做不了架构师?>>>    使用 Node.js.Express.AngularJS 和 MongoDB 构建一个实时问卷调查应用程序 2014 年 3 月 20 ...

  8. 在Visual Studio 2017中使用Asp.Net Core构建Angular4应用程序

    前言 Visual Studio 2017已经发布了很久了.做为集成了Asp.Net Core 1.1的地表最强IDE工具,越来越受.NET系的开发人员追捧. 随着Google Angular4的发布 ...

  9. 无服务器–仅仅是构建现代应用程序的一种方法?

    如果搜索"无服务器",则会发现无服务器是构建现代应用程序的一种新的流行方式. 无服务器真的是新的吗? 无服务器是指您无需担心服务器的概念–无需置备,部署和维护服务器. 显然有服务器 ...

最新文章

  1. 计算机及网络应用基础思维导图_思维导图在生物教学中的应用
  2. [YTU]_2921( Shape系列-7)
  3. Mpg123源代码详解
  4. 科普 | 你必须了解的漏洞利用缓解及对抗技术
  5. linux redis -p,linux 安装redis
  6. aop简介-aop的底层实现
  7. 华为卡槽打不开怎么办_17500元!华为5G折叠手机刷屏,有人焦虑连夜开会…
  8. Python机器学习:PCA与梯度上升:04求数据的前n个主成分
  9. html的设置语言为en,CSS中的html [lang =“ en”]和html:lang(en)有什么区别?
  10. 开放Nginx在文件夹列表功能
  11. 误差理论实际应用公式
  12. 机器学习就那么回事儿
  13. 重定向telnet方法
  14. string容器字符存取
  15. Python 面向对象3-类变量与实例变量
  16. Soem配置汇川SV660N
  17. OA办公系统 Springboot vue 前后分离 跨域 Activiti6 工作流 集成代码生成器
  18. python小程序之天天向上
  19. PowerQuery操作分类3
  20. 微信小程序的手机通讯录点击本页面跳转位置的代码详解

热门文章

  1. atom 编辑器html,Atom编辑器配置
  2. [计算机网络]HTTP、UDP、TCP协议
  3. java字符串和字符数组相互转化的方法
  4. 【ZigBee 系列】| 网关的构成?用 JN51X9 的案例来讲解框架及各部分的功能
  5. linux rm 提示权限不足
  6. IFPUG软件功能点计算方法
  7. 向量大小和归一化(vector magnitude normalization)、向量范数(vector norm)、标量/向量/矩阵/张量
  8. 基于Android的健康医疗论坛的设计与开发(android studio)
  9. centos7安装xterm_CentOS 7使用x-manager中Xstart最新工具报缺少xterm包错误
  10. linux(centos)内核升级